WEB开发网
开发学院WEB开发ASP 《NHibernate One Session Per Request 简单实现》... 阅读

《NHibernate One Session Per Request 简单实现》勘误

 2010-02-03 10:46:16 来源:WEB开发网   
核心提示:使用 NHibernate 进行 Web 开发的朋友大多都知道 session-Per-Request 模式,但网上真正能够正确使用的例子不多,《NHibernate One Session Per Request 简单实现》勘误,网上包括园子里好多文章犯了同一个错误,而这个错误确一直在散播...先来看园子里 Flye
使用 NHibernate 进行 Web 开发的朋友大多都知道 session-Per-Request 模式,但网上真正能够正确使用的例子不多,网上包括园子里好多文章犯了同一个错误,而这个错误确一直在散播...

先来看园子里 Flyear 的一篇文章 《NHibernate One Session Per Request 简单实现》。

首先对 NHibernate 进行配置没有错:

  <PRoperty name='current_session_context_class'>web</property>
错误在类 NHinbernateSessionFactory(类名都写错了) 中,NHinbernateSessionFactory.GetCurrentSession 不应包含对 HttpContext 的操作,GetCurrentSession 其实本应很简单,请参见(类名改成 NHibernateHelper,简短):

  public sealed class NHibernateHelper
  {
    public static readonly ISessionFactory SessionFactory;
    static NHibernateHelper()
    {
      SessionFactory = new Configuration()
        .Configure()
        .AddAssembly(/**/)
        .BuildSessionFactory();
    }
    public static ISession GetCurrentSession()
    {
      return SessionFactory.GetCurrentSession();
    }
  }
将 current_session_context_class 配置为 web,NHibernate 在初始化时会生成 NHibernate.Context.WebSessionContext 类的实例,WebSessionContext 类源码如下:

  [Serializable]
  public class WebSessionContext : MapBasedSessionContext
  {
    // Fields
    private const string SessionFactoryMapKey = "NHibernate.Context.WebSessionContext.SessionFactoryMapKey";

    // Methods
    public WebSessionContext(ISessionFactoryImplementor factory) : base(factory)
    { }

    protected override IDictionary GetMap()
    {
      return (HttpContext.Current.Items[SessionFactoryMapKey] as IDictionary);
    }

    protected override void SetMap(IDictionary value)
    {
      HttpContext.Current.Items[SessionFactoryMapKey] = value;
    }
  }
WebSessionContext 实现了 Session-Per-Request 模式,它封装了 HttpContext ,因此我们不需要在我们的辅助类(NHibernateSessionFactory 或是 NHibernateHelper)中再对 HttpContext 进行操作。

我们只需要从 WebSessionContext 的实例中获取 Session 即可。从WebSessionContext 类中获取当前 ISession 相当简单,因为 WebSessionContext 实现了 ICurrentSessionContext 接口:

  public interface ICurrentSessionContext
  {
    ISession CurrentSession();
  }


NHibernate.Context 命名空间中的类和接口

(说明:current_session_context_class 还可以配置为 Managed_web、Call、thread_static,分别对应类 ManagedWebSessionContext、CallSessionContext、ThreadStaticSessionContext)

在实际使用中我们并不需要直接调用 WebSessionContext 的 CurrentSession() 方法,因为 ISessionFactory 提供了一个更简单的方法让我们能一步获取到 Session:

  public interface ISessionFactory : IDisposable
  {
    ISession GetCurrentSession();
    //......
  }


下面探讨一下 ISessionFactory.GetCurrentSession 方法的具体实现:

Configuration.BuildSessionFactory 方法实际返回的是 SessionFactoryImpl 类的实例,让我们简单看一下 SessionFactoryImpl 的部分代码吧:

1   public sealed class SessionFactoryImpl
2     : ISessionFactoryImplementor, IMapping, ISessionFactory, IDisposable, IObjectReference
3   {
4     private readonly ICurrentSessionContext currentSessionContext;
5
6     public ISession GetCurrentSession()
7     {
8       if (this.currentSessionContext == null)
9       {
10         throw new HibernateException(
11           "No CurrentSessionContext configured (set the property current_session_context_class)!");
12       }
13       return this.currentSessionContext.CurrentSession();
14     }
15
16     public ICurrentSessionContext CurrentSessionContext
17     {
18       get { return this.currentSessionContext; }
19     }
20
21     private ICurrentSessionContext BuildCurrentSessionContext()
22     {
23       string name = PropertiesHelper.GetString("current_session_context_class", this.properties, null);
24       string str2 = name;
25       if (str2 != null)
26       {
27         if (str2 == "call") return new CallSessionContext(this);
28         if (str2 == "thread_static") return new ThreadStaticSessionContext(this);
29         if (str2 == "web") return new WebSessionContext(this);
30         if (str2 == "managed_web") return new ManagedWebSessionContext(this);
31       }
32       else
33         return null;
34       try
35       {
36         Type type = ReflectHelper.ClassForName(name);
37         return (ICurrentSessionContext)Environment.BytecodeProvider.ObjectsFactory
38           .CreateInstance(type, new object[] { this });
39       }
40       catch (Exception exception)
41       {
42         log.Error("Unable to construct current session context [" + name + "]", exception);
43         return null;
44       }
45     }
46   //......
47   }

SessionFactoryImpl 在实例化时会调用 BuildCurrentSessionContext() 方法(行21)为 currentSessionContext 字段赋值,具体值有配置文件中的 current_session_context_class 决定。

ISessionFactory 的 GetCurrentSession() 调用的是 ICurrentSessionContext 的 CurrentSession()(行6) 方法。

上面的代码相当简单,想必大家都已经明明白白了。



《NHibernate One Session Per Request 简单实现》 一文中还有两处不太恰当的地方:

1. 并不是每一次请求都需要一个 Session 来访问数据库。文中 Global.asax 的代码给所有的请求在开始的时候都进行WebSessionContext.Bind(),会照成很多 Session 的浪费,虽然 NHibernate 的 Session 是轻量级的,较为合理的做法是在“真正需要”时绑定。

2. 因为 WebSessionContext.Unbind 方法需要一个 ISessionFactory 接口的实例,迫使用我们的辅助类(NHibernateSessionFactory 或是 NHibernateHelper)公开 SessionFactory。

第一个问题比较容易解决,留在回复中和大家交流吧。

第二个问题我会在下一篇随笔中解答。



本人学习 NHibernate 时间也不长,属于新手,如有错误,欢迎指正!

Tags:NHibernate One Session

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接