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.
Qualquer dúvida, opinião, reclamação mande seu comentário!
Um Abraço!









Show de bola. Muito bem explicado.
Onde colocar as transactions? Ficar abrindo no begin request do httpmodule e fechar no end request não geraria um custo muito alto pra aplicação?
TTT,
Pode ser colocado no Repository, veja abaixo
public Category Salvar(Category entity)
{
using (ISession session = NHibernateHttpModule.RecuperarSessao)
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(entity);
transaction.Commit();
return entity;
}
}
}
att,
Leandro Prado
Ok, mas imagine que eu precise persistir 2 objetos em 2 repositórios diferentes. Preciso garantir as duas operações. Neste caso, a transaction dentro do repositório não iria atender… Neste caso, onde colocar?
Obrigado pela resposta!
TTT,
Você tem toda razão… uma das formas de resolver seria colocando dentro do HTTP Module, mas ai temo um porém que para todas as requisições estará usando Transaction
att,
Leandro Prado
Tem que ter uma camada acima, com classes de serviço.
class ProcessoSaque..
public void Sacar()
{
//inicia transacao
try
{
//faz algo com o repositorio1
//faz algo com o repositorio2
//commit
}
catch
{
//rollback
throw;
}
}
Parabéns Leandro pelo tutorial, muito bem feito, didático e prático.
perfeito Santos…
ficaria dessa forma Web -> Serviços -> Respositorios -> NHibernate
att,
Leandro Prado
Leandro tem como vc colocar o script do banco de dados??
O NHibernate já gera o banco de dados, schemaExport
veja aqui.. http://www.leandroprado.com.br/2010/08/asp-net-mvc-nhibernate-extjs-parte-3/
att,
Leandro Prado
Leandro, estou com um problema no HttpModule. Tenho duas operações no load da página que carregam combos de duas entidades. No carregamento da primeira vai normal, mas na segunda retorna que a conexão foi fechada. O mais estranho é que o Session permanece, provando que o EndRequest não foi chamado ainda.
Como posso resolver?
Estou usando a sua implementação
Lano,
Como assim no Load da página?? vc esta usando MVC ou WebForms??
Sempre usei essa implementação de HttpModule e nunca tive problemas…
att,
Leandro Prado
Pois é Leandro, estou usando WebForms. Até então resolvi o problema alterando um trecho do HttpModule, porém, estou tendo problemas quando vou carregar algum item de uma coleção lazy. Ele informa que não há sessão ou que a sessão está fechada. Fazendo o debug percebi que a sessão não fechou.
Olá,
Gostaria de saber como faço para configurar o NHabernate numa aplicação web usando ASP.NET MVC 3 com C#. Estou usando o banco de dados SQL Server 2008.
Já li bastante sobre o assunto, mas nenhum ainda me satisfez, queria saber a melhor maneira para configurar o NHabernate.
obrigado!
João
Basta alterar o arquivo de conexão para usar o dilato do SQL SERVER, também será necessário alterar os arquivos de mapeamento, pois nesse exemplo usa SEQUENCE e o SQL SERVER usa IDENTITY
att,
Leandro Prado
Leandro,
Utilizei seu tutorial e acabei encontrando algumas coisas problemáticas com o hibernate 3.2, como faço para te mandar os passo a passo diferentes para atualização deste tutorial?
Muito obrigado!
Diego,
Se for pouca coisa, pode colocar como um comentário mesmo.. agora se for bastante mande através da opção contato do blog, ou direto para o e-mail contato@leandroprado.com.br
até ++
Leandro,
Acho que dá por aqui mesmo, as classes maiores eu coloco como gist.
* No hibernate.cfg.xml devemos tirar a propriedade: proxyfactory.factory_class, ela não é mais necessária.
* a classe NHibernateHttpModule foi alterada em alguns pontos para atender alguns warnings do ReSharper: https://gist.github.com/1782745
* a parte de tags do HttpModule mudou para
* Mapeamento por código da classe categoria: https://gist.github.com/1782770
e ao invés de fazer um Repositório por entidade, eu criei um Genérico e após especializo no Controller com a seguinte chamada:
Link da interface: https://gist.github.com/1782788
Link da impl: https://gist.github.com/1782792
Exemplo de uso
private readonly IRepository _repository;
IList list = _repository.All();
Acho que isso foi tudo, deu para entender?
Abraços!
perfeito Diego.. entendi sim…
Obrigado pelas considerações das alterações da nova versão do NHibernate
att,
Leandro Prado
A parte do HttpModule foi cortada:
<add name=”NHibernateHttpModule” type=”ConexaoNHibernate.NHibernateHttpModule”/>
A parte do repositório também ficou quebrada:
private readonly IRepository<Category> _repository;
IList<Category> list = _repository.All();
Bom fiz todos os passos do tutorial corretamente como se pede, e mesmo assim não consigo fazer funcionar. Estou usando Visual Studio 2010, NHibernate 2.2 e banco de dados SQL Server 2008 gostaria de saber se por acaso pode acontecer alguma compatibilidade de plataforma.
Obrigado a todos.
Alexandre,
Qual o erro está ocorrendo?
att,
Leandro Prado