A maioria dos amigos que usam o NHibernate para desenvolvimento web conhece o modo Session-Per-Request, mas não há muitos exemplos de como usá-lo corretamente na Internet. Muitos artigos na Internet, inclusive no jardim, cometeram o mesmo erro, e esse erro vem se espalhando...
Vejamos primeiro um artigo de Flyear no jardim, "NHibernate One Session Per Request Simple Implementation".
Não há nada de errado em configurar o NHibernate primeiro:
<property name='current_session_context_class'>web</property>
O erro está na classe NHinbernateSessionFactory (os nomes das classes estão todos errados). NHinbernateSessionFactory.GetCurrentSession não deve incluir operações em GetCurrentSession, na verdade, deve ser muito simples.
classe pública selada NHibernateHelper
{
public static readonly ISessionFactory SessionFactory;
staticNHibernateHelper()
{
SessionFactory = nova configuração()
.Configurar()
.AddAssembly(/**/)
.BuildSessionFactory();
}
ISession pública estática GetCurrentSession()
{
retornar SessionFactory.GetCurrentSession();
}
}
Configure current_session_context_class como web e o NHibernate irá gerar uma instância da classe NHibernate.Context.WebSessionContext durante a inicialização. O código fonte da classe WebSessionContext é o seguinte:
[Serializável]
classe pública WebSessionContext: MapBasedSessionContext
{
//Campos
string const privada SessionFactoryMapKey = "NHibernate.Context.WebSessionContext.SessionFactoryMapKey";
// Métodos
public WebSessionContext (fábrica ISessionFactoryImplementor): base (fábrica)
{ }
substituição protegida IDictionary GetMap()
{
retornar (HttpContext.Current.Items[SessionFactoryMapKey] como IDictionary);
}
substituição protegida void SetMap (valor IDictionary)
{
HttpContext.Current.Items[SessionFactoryMapKey] = valor;
}
}
WebSessionContext implementa o modo Session-Per-Request, que encapsula HttpContext, portanto não precisamos operar HttpContext em nossa classe auxiliar (NHibernateSessionFactory ou NHibernateHelper).
Precisamos apenas obter a Sessão da instância de WebSessionContext. Obter o ISession atual da classe WebSessionContext é bastante simples, porque WebSessionContext implementa a interface ICurrentSessionContext:
interface pública ICurrentSessionContext
{
ISession CurrentSession();
}
Classes e interfaces no namespace NHibernate.Context
(Nota: current_session_context_class também pode ser configurado como Managed_web, Call, thread_static, correspondendo às classes ManagedWebSessionContext, CallSessionContext e ThreadStaticSessionContext respectivamente)
No uso real, não precisamos chamar diretamente o método CurrentSession() de WebSessionContext, porque ISessionFactory fornece um método mais simples que nos permite obter a Sessão em uma única etapa:
interface pública ISessionFactory: IDisposable
{
ISession GetCurrentSession();
//......
}
Vamos discutir a implementação específica do método ISessionFactory.GetCurrentSession:
O método Configuration.BuildSessionFactory na verdade retorna uma instância da classe SessionFactoryImpl. Vamos dar uma breve olhada em parte do código de SessionFactoryImpl:
1 classe pública selada SessionFactoryImpl
2: ISessionFactoryImplementor, IMapping, ISessionFactory, IDisposable, IObjectReference
3 {
4 privado somente leitura ICurrentSessionContext currentSessionContext;
5
6 ISession pública GetCurrentSession()
7 {
8 se (this.currentSessionContext == nulo)
9 {
10 lança nova HibernateException(
11 "Nenhum CurrentSessionContext configurado (defina a propriedade current_session_context_class)!");
12}
13 retorne this.currentSessionContext.CurrentSession();
14}
15
16 ICurrentSessionContext público CurrentSessionContext
17 {
18 obter {retornar this.currentSessionContext};
19}
20
21 ICurrentSessionContext privado BuildCurrentSessionContext()
vinte e dois {
23 string nome = PropertiesHelper.GetString("current_session_context_class", this.properties, null);
24 string str2 = nome;
25 se (str2! = nulo)
26 {
27 if (str2 == "call") return new CallSessionContext(this);
28 if (str2 == "thread_static") retornar novo ThreadStaticSessionContext(this);
29 if (str2 == "web") retorna novo WebSessionContext(this);
30 if (str2 == "gerenciado_web") retornar novo ManagedWebSessionContext(this);
31}
32 mais
33 retorna nulo;
34 tentar
35 {
36 Tipo tipo = ReflectHelper.ClassForName(nome);
37 retorno (ICurrentSessionContext)Environment.BytecodeProvider.ObjectsFactory
38.CreateInstance(tipo, novo objeto[] {este});
39}
40 captura (exceção de exceção)
41 {
42 log.Error("Não foi possível construir o contexto da sessão atual [" + nome + "]", exceção);
43 retorna nulo;
44}
45}
46 //......
47}
Quando SessionFactoryImpl é instanciado, ele chama o método BuildCurrentSessionContext() (linha 21) para atribuir um valor ao campo currentSessionContext. O valor específico é determinado pela current_session_context_class no arquivo de configuração.
GetCurrentSession() de ISessionFactory chama o método CurrentSession() (linha 6) de ICurrentSessionContext.
O código acima é bastante simples, todos já devem entendê-lo.
Existem dois lugares inadequados no artigo "NHibernate One Session Per Request Simple Implementation":
1. Nem toda solicitação requer uma Sessão para acessar o banco de dados. O código Global.asax no artigo executa WebSessionContext.Bind() no início de todas as solicitações, o que desperdiçará muito tempo de sessão. Embora a sessão do NHibernate seja leve, uma abordagem mais razoável é "realmente precisar" de vinculação de tempo.
2. Como o método WebSessionContext.Unbind requer uma instância da interface ISessionFactory, somos forçados a usar nossa classe auxiliar (NHibernateSessionFactory ou NHibernateHelper) para expor SessionFactory.
A primeira dúvida é mais fácil de resolver, então deixe na resposta para comunicar com todos.
Responderei à segunda pergunta no próximo ensaio.
Faz muito tempo que não aprendo o NHibernate e sou um novato. Se houver algum erro, corrija-me!