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:

[sourcecode language=”csharp”]
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;

}
}
}
[/sourcecode]

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:

[sourcecode language=”xml”]
<httpModules>

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

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

[sourcecode language=”xml”]
<?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>
[/sourcecode]

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:

[sourcecode language=”csharp”]
namespace ConexaoNHibernate.Entity
{
public class Category
{
public virtual int IdCategoria { get; set; }
public virtual string Nome { get; set; }
public virtual string Descricao { get; set; }
}
}
[/sourcecode]

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

[sourcecode language=”xml”]
<?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>
[/sourcecode]

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:

[sourcecode language=”csharp”]
public interface ICategoryRepository
{
Category Salvar(Category entity);
Category Alterar(Category entity);
void Excluir(Category entity);
Category ObterPorId(int id);
IList<Category> ObterTodos();
}
[/sourcecode]

7. Implementando as Interfaces

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

[sourcecode language=”csharp”]
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>();
}
}
}
[/sourcecode]

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:

[sourcecode language=”csharp”]
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);
}
}
}
[/sourcecode]

9. Criando a View

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

[sourcecode language=”html”]
<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>
[/sourcecode]

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!

Sobre Leandro Prado

Leandro Silveira Prado é Premier Field Engineer na Microsoft especializado em Application Lifecycle Management.