Usando NHibernate com ASP NET MVC

Quando estamos usando o NHibernate como camada de acesso a dados, temos várias maneiras de configurá-lo, uma delas é criando uma classe base onde podemos recuperar a Sessão, como esta descrito nesse post ASP NET MVC + NHibernate + ExtJs – Parte 4, e outra forma é criando uma HttpModule que vamos ver nesse post como criar.

Um HttpModule é uma extensão do framework .NET que é executada no momento de uma solicitação HTTP, em nosso caso quando iniciar a solicitação HTTP vamos acionar um método que vai abrir uma conexão com NHibernate, e quando terminar essa solicitação HTTP vamos chamar outro método que vamos fechar essa conexão.

1. Criando o projeto no Visual Studio

Primeiro de tudo vamos criar uma solution em branco, coloque o nome de ConexaoNHibernate

Crie 4 projetos do tipo ClassLibrary com o nome de

  • ConexaoNHibernate.HttpModule: Configuração da conexão do NHibernate como HttpModule
  • ConexaoNHibernate.Entity: Entidades e seus mapeamentos
  • ConexaoNHibernate.Data: Interfaces com suas assinaturas
  • ConexaoNHibernate.Data.NHibernate: Implementação das Interfaces usando o NHibernate

Crie um novo projeto ASP NET MVC com o nome de ConexaoNHibernate.Web

Veja o projeto criado

2. Criando o HttpModule

Quando queremos criar um HttpModule nossa classe tem que herdar de IHttpModule que esta no namespace System.Web.IHttpModule, por esse motivo no projeto ConexaoNHibernate.HttpModule vamos adicionar uma nova referência a dll System.Web

Também vamos precisar adicionar uma referência a DLL do NHibernate

No projeto ConexaoNHibernate.HttpModule crie uma nova classe chamada NHibernateHttpModule

Veja abaixo o conteúdo da classe:

using System;
using System.Web;
using NHibernate;
using NHibernate.Cfg;

namespace ConexaoNHibernate.HttpModule
{
    public class NHibernateHttpModule : IHttpModule
    {
        public static readonly string CHAVE = "NHibernateSession";
        private static ISession _session;
        private static ISessionFactory factory = null;

        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(context_BeginRequest);
            context.EndRequest += new EventHandler(context_EndRequest);
        }

        public void Dispose()
        {
        }

        public static ISession RecuperarSessao
        {
            get
            {
                if (HttpContext.Current == null)
                {
                    if (_session != null)
                    {
                        return _session;
                    }
                    else
                    {
                        _session = AbrirSessao();
                        return _session;
                    }
                }
                else
                {
                    HttpContext currentContext = HttpContext.Current;
                    ISession session = currentContext.Items[CHAVE] as ISession;
                    if (session == null)
                    {
                        session = AbrirSessao();
                        currentContext.Items[CHAVE] = session;
                    }
                    return session;
                }
            }
        }

        private void context_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            HttpContext context = application.Context;
            context.Items[CHAVE] = AbrirSessao();
        }

        private void context_EndRequest(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            HttpContext context = application.Context;

            ISession session = context.Items[CHAVE] as ISession;
            if (session != null)
            {
                session.Flush();
                session.Close();
            }

            context.Items[CHAVE] = null;
        }

        private static ISession AbrirSessao()
        {
            ISession session;
            session = GetFactory().OpenSession();

            if (session == null)
                throw new InvalidOperationException("OpenSession() is null.");

            return session;
        }

        private static ISessionFactory GetFactory()
        {
            if (factory == null)
            {
                Configuration config = new Configuration();

                if (config == null)
                    throw new InvalidOperationException("NHibernate configuration is null.");

                config.Configure();

                factory = config.BuildSessionFactory();

                if (factory == null)
                    throw new InvalidOperationException("BuildSessionFactory is null.");
            }

            return factory;

        }
    }
}

Para maiores detalhes sobre o HttpModule veja esse tutorial feito pela Microsoft Criar um módulo HTTP do ASP.NET usando o Visual C# .NET.

3. Configurando o HttpModule

Adicione ao projeto NHibernateHttpModule.Web uma referência ao projeto NHibernateHttpModule.HttpModule

Para que nosso HttpModule funcione, temos que configurá-lo no web.config do projeto web, localize a sessão httpModules e vamos adicionar nossa classe como um HttpModule, veja abaixo:

<httpModules>
    ...
    <add name="NHibernateHttpModule" type="ConexaoNHibernate.HttpModule.NHibernateHttpModule, ConexaoNHibernate.HttpModule"/>
</httpModules>

4. Configurando a conexão

Agora temos que configurar qual banco de dados que vamos usar, dentro do projeto ConexaoNHibernate.Web adicione um arquivo XML com o nome hibernate.cfg.xml ,veja abaixo a configuração para o banco de dados PostgreSQL

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <property name="connection.provider"> NHibernate.Connection.DriverConnectionProvider</property>
    <property name="dialect"> NHibernate.Dialect.PostgreSQL81Dialect</property>
    <property name="connection.driver_class"> NHibernate.Driver.NpgsqlDriver</property>
    <property name="connection.connection_string"> server=localhost;port=5432;database=tutorial;uid=postgres;pwd=123456</property>
    <property name="proxyfactory.factory_class"> NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
    <property name="show_sql">true</property>
    <mapping assembly="ConexaoNHibernate.Entity" />
  </session-factory>
</hibernate-configuration>

OBS: Não esqueça de alterar a propriedade Copy To Output Directory para Copy Always

5. Mapamento da Entidade

Vamos testar mapeando uma entidade simples para o nosso exemplo, dentro do projeto ConexaoNHibernate.Entity crie uma entidade com o nome Category, veja abaixo seu conteúdo:

namespace ConexaoNHibernate.Entity
{
    public class Category
    {
        public virtual int IdCategoria { get; set; }
        public virtual string Nome { get; set; }
        public virtual string Descricao { get; set; }
    }
}

Agora adicione um arquivo xml com o nome de Category.hbm.xml e faça o mapeamento da classe como descrito abaixo:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="ConexaoNHibernate.Entity"
                   namespace="ConexaoNHibernate.Entity" >

  <class name="Category" table="category">

    <id name="IdCategoria" access="property" column="id_categoria" type="Int32">
      <generator class="sequence">
        <param name="sequence">category_seq_id</param>
      </generator>
    </id>

    <property name="Nome" not-null="true" access="property" type="String">
      <column name="nome" length="100" />
    </property>

    <property name="Descricao" not-null="true" access="property" type="String">
      <column name="descricao" length="100" />
    </property>

  </class>
</hibernate-mapping>

OBS: Não esqueça de alterar a propriedade Build Action para Embedded Resource

6. Criando as Interfaces

Dentro do projeto ConexaoNHibernate.Data adicione uma interface com o nome ICategoryRepository, veja abaixo:

public interface ICategoryRepository
{
    Category Salvar(Category entity);
    Category Alterar(Category entity);
    void Excluir(Category entity);
    Category ObterPorId(int id);
    IList<Category> ObterTodos();
}

7. Implementando as Interfaces

Dentro do projeto ConexaoNHibernate.Data.NHibernate vamos implementar nossas interfaces, adicione uma nova classe com o nome CategoryRepository, veja abaixo:

using System.Collections.Generic;
using ConexaoNHibernate.Entity;
using ConexaoNHibernate.HttpModule;

namespace ConexaoNHibernate.Data.NHibernate
{
    public class CategoryRepository : ICategoryRepository
    {
        public Category Salvar(Category entity)
        {
            NHibernateHttpModule.RecuperarSessao.Save(entity);
            return entity;
        }

        public Category Alterar(Category entity)
        {
            NHibernateHttpModule.RecuperarSessao.Update(entity);
            return entity;
        }

        public void Excluir(Category entity)
        {
            NHibernateHttpModule.RecuperarSessao.Delete(entity);
        }

        public Category ObterPorId(int id)
        {
            return NHibernateHttpModule.RecuperarSessao.Get<Category>(id);
        }

        public IList<Category> ObterTodos()
        {
            return NHibernateHttpModule.RecuperarSessao.CreateCriteria(typeof(Category)).List<Category>();
        }
    }
}

8. Criando o Controller

Dentro do projeto ConexaoNHibernate.Web crie um novo controller com o nome CategoryController e dentro da action Index vamos chamar o método ObterTodos, veja abaixo:

using System.Collections.Generic;
using System.Web.Mvc;
using ConexaoNHibernate.Data;
using ConexaoNHibernate.Data.NHibernate;
using ConexaoNHibernate.Entity;

namespace ConexaoNHibernate.Web.Controllers
{
    public class CategoryController : Controller
    {
        private readonly ICategoryRepository _categoryRepository;

        public CategoryController()
        {
            if (_categoryRepository == null)
                _categoryRepository = new CategoryRepository();
        }

        public ActionResult Index()
        {
            IList<Category> lst = _categoryRepository.ObterTodos();
            return View(lst);
        }
    }
}

9. Criando a View

Vamos criar uma página simples para mostrar os dados, veja abaixo:

<table>
    <tr>
        <th></th>
        <th>
            IdCategoria
        </th>
        <th>
            Nome
        </th>
        <th>
            Descricao
        </th>
    </tr>

<% foreach (var item in Model) { %>

    <tr>
        <td>
            <%= Html.ActionLink("Edit", "Edit", new { id=item.IdCategoria }) %> |
            <%= Html.ActionLink("Details", "Details", new { id=item.IdCategoria })%> |
            <%= Html.ActionLink("Delete", "Delete", new { id=item.IdCategoria })%>
        </td>
        <td>
            <%= Html.Encode(item.IdCategoria) %>
        </td>
        <td>
            <%= Html.Encode(item.Nome) %>
        </td>
        <td>
            <%= Html.Encode(item.Descricao) %>
        </td>
    </tr>

<% } %>

</table>

Abra a classe NHibernateHttpModule e adicione um breakpoint no método context_BeginRequest e outro context_EndRequest e execute o projeto Web.

Veja que quando iniciar o processo que carrega uma página ASPX vai ser chamado o método context_BeginRequest e assim que esse processo acabar será chamado o método context_EndRequest. Esse procedimento vai acontecer em todos os posts que acontecerem na página.

Download do projeto

Qualquer dúvida, opinião, reclamação mande seu comentário!

Um Abraço!

Leia mais

Sobre Leandro Prado

Leandro Silveira Prado é graduado em Sistemas de Informação pela PUC-PR, trabalho com desenvolvimento WEB desde 2003. Possui uma vasta experiência em integração de sistemas ja prestou serviços a grandes empresas como FBits Fábrica de Software, Instituto Curitiba de Informática, América Latina Logística e atualmente trabalha como Consultor ALM na especificações.com. Fanático por futebol e torcedor do melhor time do paraná - COXA