ASP .NET - Usando o NHibernate em uma aplicação Web I


Após muitos artigos dedicados ao Entity Framework estou voltando a falar do NHibernate, desta vez usando a linguagem C# em um projeto ASP .NET Web Forms.

O NHibernate é um porte do consagrado framework Hibernate para Java para a plataforma .NET, dessa forma o NHibernate surgiu a partir do Hibernate um framework muito usado na plataforma Java.

Podemos pensar no NHibernate como uma ferramenta ORM de persistência e ele não esta só pois existem muitas outras ferramentas que se propõe a fazer a mesma coisa. Veja a relação abaixo:

Lista das ferramentas ORM e/ou Frameworks de persistência mais conhecidos:
  • ADO.NET Entity Framework, Microsoft's ORM, part of .Net 4.0
  • AgileFx, open source
  • Base One Foundation Component Library, free or commercial
  • Devart LinqConnect, commercial, an ORM solution for Oracle, MySQL, PostgreSQL, and SQLite
  • Castle ActiveRecord, ActiveRecord for .NET, open source
  • Database Objects .NET, Open Source
  • DataObjects.NET, GPL and commercial
  • DevForce, commercial, N-Tier
  • ECO, Commercial but free for use up to 12 classes
  • EntitySpaces, commercial
  • Habanero, Free open source Enterprise application framework with a Free Code Generation Tool
  • iBATIS, Free open source
  • LINQ to SQL, Free, .NET Framework component
  • LLBLGen Pro, commercial
  • Neo, open source
  • NHibernate, open source
  • nHydrate, open source
  • ObjectMapper .NET, GPL and commercial license
  • OpenAccess ORM, free or commercial
  • Persistor.NET, free or commercial
  • Quick Objects, free or commercial
  • SubSonic, open source
  • A grande vantagem do NHibernate é que ela é uma ferramenta gratuita, madura e com uma grande aceitação e utilização na comunidade .NET.

    Usando o NHibernate com a linguagem VB .NET em uma aplicação ASP .NET Web Forms

    Neste artigo eu vou mostrar como criar uma aplicação simples que mostra como usar o NHibernate para fazer o mapeamento objeto relacional e persistir dados no SQL Server 2005 Express Edition.

    Recursos necessários:

    Existem duas formas de usarmos o NHibernate:

    1. A partir do modelo de dados já criado realizar o mapeamento ORM para as entidades;
    2. Criar a estrutura das tabelas no banco de dados a partir das entidades definidas nos arquivos de configuração; (Model First)

    Neste artigo eu vou usar a primeira opção onde a partir do modelo de banco de dados e tabela já existente irei realizar o mapeamento gerando as entidades, através dos arquivos de configuração XML (poderíamos também usar anotações) do NHibernate.

    Roteiro de utilização:

    Definindo o modelo de dados: Banco de dados e tabela

    Neste exemplo teremos um banco de dados criado no SQL Server 2005 de nome Macoratti.mdf e um única tabela chamada Usuarios com a seguinte estrutura:

    Tabela Usuarios

    A nossa aplicação ASP .NET Web Forms irá gerenciar os usuários permitindo a inclusão , alteração e exclusão de usuários da tabela Usuarios.

    Note que definimos uma chave primária - usuarioid - do tipo identity; dessa forma este campo passa a ser gerenciado pelo Banco de dados.

    2- Criação do projeto no Visual Web Developer e referência ao NHibernate 2.1.2

    Abra o Visual Web Developer 2010 Express Edition e selecione a opção New Project;

    A seguir selecione Visual C# -> Web e escolha o template ASP .NET Empty Web Application e informe o local e o nome do projeto que no nosso caso é NHibernateASPNET;

    Vamos incluir dois arquivos ao projeto visto que o mesmo esta vazio.

    Nome Project selecione Add New Item e a seguir selecione o template Web Configuration File e defina as opções conforme a figura baixo para incluir o arquivo Web.Config no projeto clicando no botão Add;

    Repita o procedimento acima selecionando o template Web Form e informando o nome Default.aspx para incluir o um novo Web Form no projeto;

    Iremos usar estes arquivos mais à frente.

    Agora baixe o NHibernate 2.1.2 do site: http://sourceforge.net/projects/nhibernate/files/ e descompacte o arquivo em um local apropriado na sua máquina local;

    Eu estou descompactando o arquivo na pasta C:\Program Files\NHibernate-2.1.2.GA-bin; Abaixo vemos os arquivos desta pasta:

    Em seguida no menu Project -> Add Reference e na janela Add Reference selecione a guia Browse e localize a pasta onde você descompactou os arquivos do NHibernate;

    Abra a pasta Required_Bins e selecione todos os arquivos e clique em OK;

    Repita o processo acima incluindo uma referência para a pasta Required_For_LazyLoading -> Castle, selecione todos os arquivos e clique em OK;

    Após isso precisamos fazer mais uma referência no projeto; trata-se do assembly NHibernate.ByteCode.LinFu.dll que esta pasta Required_For_LazyLoading, subpasta - LinFu:

     Se não fizermos isso iremos obter o erro:  The ProxyFactoryFactory was not configured. Initialize 'proxyfactory.factory_class' property of the session-factory  configuration section with one of the available NHibernate.ByteCode providers.

    Assim já temos todas as principais referências no projeto e com essas referências garantimos o funcionamento do NHibernate na maioria dos projetos.

    A próxima tarefa é definir os arquivos de mapeamento do NHibernate, a forma mais comum de fazer isso é usar arquivos XML.

    Podemos obter o recurso do IntelliSense no Visual ao realizar a definição dos arquivos XML copiando o arquivo nhibernate-mapping.xsd da pasta Required_Bins para a pasta c:\Arquivos de Programas\Microsoft\Visual Studio 10.0\Xml\Schemas

    1- C:\Program Files\NHibernate-2.1.2.GA-bin\Required_Bins

    2- C:\Program Files\Microsoft Visual Studio 10.0\Xml\Schemas

    Após isso já podemos criar os arquivos de mapeamento tendo o recurso IntelliSense a disposição o que nos ajuda muito.

    Criando a classe de Negócio

    Como estamos partindo do modelo de dados já criado agora vamos definir a classe de negócio que representa a tabela Usuarios.

    No menu Project selecione Add Class , selecione o template Class e informe o nome Usuario.cs e clique em Add;

    A seguir vamos definir o seguinte código no arquivo Usuario.vb:

      Public Class Usuario
    
    Private _usuarioid As Int32
    Private _nome As String
    Private _login As String
    Private _senha As String
    Private _perfil As String
    Private _email As String
    
    Public Sub New()
    End Sub
    
    Public Overridable Property UsuarioId() As Int32
    Get
    Return _usuarioid
    End Get
    Set(ByVal value As Int32)
    _usuarioid = value
    End Set
    End Property
    
    Public Overridable Property Nome() As String
    Get
    Return _nome
    End Get
    Set(ByVal value As String)
    _nome = value
    End Set
    End Property
    
    Public Overridable Property Login() As String
    Get
    Return _login
    End Get
    Set(ByVal value As String)
    _login = value
    End Set
    End Property
    
    Public Overridable Property Senha() As String
    Get
    Return _senha
    End Get
    Set(ByVal value As String)
    _senha = value
    End Set
    End Property
    
    Public Overridable Property Perfil() As String
    Get
    Return _perfil
    End Get
    Set(ByVal value As String)
    _perfil = value
    End Set
    End Property
    
    Public Overridable Property Email() As String
    Get
    Return _email
    End Get
    Set(ByVal value As String)
    _email = value
    End Set
    End Property
    End Class

    Neste código estamos declarando as propriedades na classe Usuario, onde cada propriedade será mapeada para o respectivo campo da tabela Usuarios.

    No VB.NET indicamos que um método é passível de sobreposição usando a palavra-chave Overridable na classe pai (classe base) e a seguir na classe filha declaramos novamente o método com a palavra-chave Overrides. Assim temos que :
    • Overridable - declara que o método pode ser sobreposto nas classes que herdarem da classe base
    • Overrides - indica que o método da classe filha irá sobrepor o método da classe pai.

    Para que um método da classe pai não possa ser sobreposto usamos o modificador - NotOverridable.  Já, para definir que um método seja obrigatoriamente sobreposto em uma classe filha usamos a palavra-chave - MustOverride.

    Com a classe Usuario definida podemos passar para a etapa de configuração e mapeamento.

    Definindo os arquivos de Mapeamento e Configuração

    Creio que a parte de configuração é que apresenta maiores problemas para os usuários iniciantes, portanto preste atenção em cada detalhe usado na configuração do NHibernate.

    Temos de que definir dois tipos de configuração:

    - Definindo o arquivo de configuração

    A primeira coisa que vamos fazer é definir o arquivo de configuração para que o NHibernate possa funcionar corretamente.

    Podemos definir essas configurações no arquivo Web.Config ou em um arquivo a parte chamado de hibernate.cfg.xml;

    Como já temos o arquivo Web.Config vamos usá-lo para criar essas configurações.

    Abra o arquivo Web.Config e inclua o código que esta em azul conforme mostrado a seguir:

    <?xml version="1.0"?>
    <!--
      For more information on how to configure your ASP.NET application, please visit
      http://go.microsoft.com/fwlink/?LinkId=169433
      -->
    <configuration>
        <system.web>
            <compilation debug="true" targetFramework="4.0" />
        </system.web>

      <!--tag definindo seção de configuração do NHibernate -->
     
    <configSections>
        <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
      </configSections>

     
     
    <!-- Configuração do NHibernate-->
     
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
        <session-factory>
          <property name="dialect">
            NHibernate.Dialect.MsSql2005Dialect
          </property>
          <property name="connection.provider">
            NHibernate.Connection.DriverConnectionProvider
          </property>
          <property name="connection.driver_class">
            NHibernate.Driver.SqlClientDriver
          </property>
          <property name="connection.connection_string">
            Server=.\SQLEXPRESS;
            Database=Acesso;
            Integrated Security=True;

          </property>
          
    <property name="proxyfactory.factory_class">

                   NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu

            </property>

        </session-factory>
     </hibernate-configuration>

     
    </configuration>

    Arquivo Web.Config

    Observe que tivemos primeiro definir a sessão de configuração para depois criar sessão com as configurações do NHibernate.

    <!-- Configuração do NHibernate-->
     
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
        <session-factory>
          <property name="dialect">
            NHibernate.Dialect.MsSql2005Dialect
          </property>
          <property name="connection.provider">
            NHibernate.Connection.DriverConnectionProvider
          </property>
          <property name="connection.driver_class">
            NHibernate.Driver.SqlClientDriver
          </property>
          <property name="connection.connection_string">
            Server=.\SQLEXPRESS;
            Database=Acesso;
            Integrated Security=True;

          </property>
         
    <property name="proxyfactory.factory_class">

             NHibernate.ByteCode.LinFu.ProxyFactoryFactory,                         NHibernate.ByteCode.LinFu

         </property>
        </session-factory>
     </hibernate-configuration>

    Na sessão de configuração do arquivo web.config definimos os seguintes itens:
    • No arquivo acima definimos o provedor de acesso a base de dados SQL Server 2005: NHibernate.Dialect.MsSql2005Dialect ;
    • O provedor usado:  NHibernate.Connection.DriverConnectionProvider;
    • O driver apropriado:  NHibernate.Driver.SqlClientDriver;
    • Definimos também a string de conexão usada:    Server=.\SQLEXPRESS;
       Database=Acesso;
       Integrated Security=True;
    • A proxyFactory : NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu

       

    - Definindo o arquivo de mapeamento

    Como já temos a classe de negócio criada vamos definir o arquivo de mapeamento baseado nesta classe.

    O mapeamento é o coração do NHibernate e se não for feito corretamente vai lhe dar muita dor de cabeça.

    No arquivo de mapeamento simplesmente especificamos qual tabela no banco de dados estamos relacionando com a classe de negócio.

    Podemos definir este mapeamento em um arquivo XML separado ou usando atributos nas classes, propriedades e membros.

    Vamos criar um arquivo XML no projeto. No menu Project selecione Add New Item;

    A seguir selecione o item Data e o template XML File e informe o nome Usuario.hbm.xml e clique em Add;

    A seguir devemos realizar duas tarefas com o arquivo Usuario.hbm.xml:

    1- Alterar a sua propriedade Build Action para : Embedded Resource;

    2- Definir a tabela e o mapeamento entre a classe de negócio e as tabelas usadas:

    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
    <class name="NHibernateASPNET.Usuario, NHibernateASPNET" lazy="true" table ="Usuarios">
    <id name="UsuarioId" type="Int32" >
         <generator class="native" />
    </id>
    <property name="Nome" column ="nome" type="String" length="50" />
    <property name="Login" column="login" type="String" length="50" />
    <property name="Senha" column="senha" type="String" length="50" />
    <property name="Perfil" column="perfil" type="String" length="50" />
    <property name="Email" column="email" type="String" length="80" />

    </class>
    </hibernate-mapping>

    Como nosso exemplo usa somente uma tabela (de propósito) fica fácil definir o mapeamento conforme o código acima, onde temos:

    Agora vamos entender o significado deste código deste arquivo:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">

    Esta linha indica o inicio do mapeamento(hibernate-mapping) feito NHibernate e deve ser usada na versão 2.1.2(rn:nhibernate-mapping-2.2).

    Atenção: (Se você estiver usando uma versão anterior do NHibernate não use esta sintaxe)

    <class name="NHibernateASPNET.Usuario, NHibernateASPNET" table="Usuarios">

    -Aqui definimos o nome da classe na tag class :<class name="NHibernateASPNET.Usuario, NHibernateASPNET"  (Note que incluímos o nome do Assembly )

    -O atributo name deve conter o namespace e o nome do Assembly.

    -O atributo table deve indicar o nome da tabela : table="Usuarios"   (se o nome da tabela for igual ao da classe não precisa ser declarado)

    Vejamos agora o mapeamento da chave primária:

    <id name="Usuarioid" column="usuarioid" type="Int32">
          <generator class="native" />
    </id>

    Nesta definição temos o mapeamento da chave primária onde a propriedade Usuarioid é mapeada para a coluna da tabela usuarioid.

    A tag id identifica a chave primária e o atributo name="Usuarioid" informa o nome do atributo da classe .NET que se refere à chave primária da tabela.

    O atributo column informa ao NHibernate que coluna na tabela é a chave primária, no caso usuarioid.

    O atributo generator informa qual a estratégia para geração da chave primária. Para o nosso exemplo usamos a estratégia native que significa que o NHibernate irá usar a estratégia que melhor se adequar ao banco de dados podendo ser: identity, sequence ou hilo dependendo das capacidades do banco de dados.

    . Outros valores possíveis são:

    1. Increment - Lê o valor máximo da chave primária e incrementa;
    2. Identity - Mapeado para colunas identity no DB2, MySQL, MSSQL, SyBase, Informix;
    3. sequence - Mapeado em sequências no DB2, PostGreSQL, Oracle, SAP DB, Firebird;
    4. hilo - Usa um algoritmo high/low para gerar chaves únicas;
    5. uudi.hex - Usa uma combinação do IP com um timestamp para gerar um identificador único.
    6. guid - uUa o novo system.guid como identificador;

    Após isso temos os mapeamentos das propriedades persistentes através da tag property.

    <property name="Nome" column ="nome" type="String" length="50" />
    <property name="Login" column="login" type="String" length="50" />
    <property name="Senha" column="senha" type="String" length="50" />
    <property name="Perfil" column="perfil" type="String" length="50" />
    <property name="Email" column="email" type="String" length="80" />

    As tags property  indicam propriedades simples dos objetos onde o nome das propriedades das classes são definidos pelo atributo name, o tipo pelo atributo type e a coluna da tabela a que se refere pelo atributo column.

    No nosso exemplo temos o mapeamento das propriedades Nome,Login, Senha, Perfil e Email para as colunas nome,login,senha,perfil e email do mesmo tipo.

    Se o atributo column não aparecer no mapeamento da propriedade, o NHibernate considera que a coluna na tabela do banco de dados a que se referencia possui o mesmo nome que o definido pelo atributo name.

    Dessa forma definimos os arquivos de configuração e mapeamento e já temos a classe de negócio definida e o banco de dados criado.

    Só falta definir a interface no projeto ASP .NET onde iremos usar a página Default.aspx  para realizar a manutenção dos dados dos usuários na tabela Usuarios e definir uma sessão NHibernate para realizar as operações desejadas persistindo-as no banco de dados.

    Aguarde a continuação deste artigo em : ASP .NET - Usando o NHibernate em uma aplicação Web II (inativo)

    Referências:

    José Carlos Macoratti