NHibernate的Session管理策略和NHibernateHelper
声明
本篇中提到的Session,并非是Asp.NET中的Session,而是NHibernate中ISession。
本篇假设读者已经对NHibernate的有一定的了解,已经成功持久化过领域模型。
你不需要亲自用复制本篇的代码去调试,因为本篇的代码并不全,最后也有提供下载。
本篇中的单元测试用到了NUnit,如果你使用其他框架也并不影响理解。
正文 开门见山
在ASP.NET中,如果我们希望每一个HTTP的请求只打开一个Session,在请求结束时关闭这个Session。
也就是在请求期间,只用到这一个Session。或许我们可以称他为“one session per request”。
这样做得好处是,你可以更容易地使用延迟加载,因为当领域模型传到表示层时,ISession在表示层仍然是打开的,并再请求结束后自动销毁。
另外在单元测试时,并不一定有HTTP的请求,所以需要用另一种策略代替。
作法:
我们会在ASP.NET应用程序中这样使用
HttpContext context = HttpContext.Current; if (context.Items["nhsession"] == null) { context.Items["nhsession"] = sessionFactory.OpenSession(); } return context.Items["nhsession"] as ISession;
以上代码意思是:我们需要Session时,就从web的上下文中取,一个请求中第一次会打开一个Session并保存再web上下文,以后就不再新打开了。
但是有了这样的一个GetSession方法后,并不代表万事大吉了。
下面来看一下单元测试:
[Test] public void GetSessionTest() { ISession session= NHibernateHelper.GetSession(); }
报错了,未将对象引用到实例,是哪个对象呢?HttpContext.Currtent为null。
因为建立的是类库,这里并没有web的上下文。那怎么办呢?
所以在这里我使用两套Session策略
1、在Web应用程序中,就使用刚才提到的策略。
2、在WinForm或单元测试中,使用另一种策略,每个线程用一个Session。
其实在NHibernate文档中也有提到,在1.2以后的版本中,增加了一个GetCurrentSession()的方法,用于获取当前Session,这个上下文只支持一个ISessionFactory。你必须自己通过CurrentSessionContext类中的静态方法手动的把ISession实例绑定或者取消绑定到当前上下文。
修改代码
private static void BindContext() { if (!CurrentSessionContext.HasBind(sessionFactory)) { CurrentSessionContext.Bind(sessionFactory.OpenSession()); } } private static void UnBindContext() { if (CurrentSessionContext.HasBind(sessionFactory)) { CurrentSessionContext.Unbind(sessionFactory); } } public static ISession GetCurrentSession() { BindContext(); return sessionFactory.GetCurrentSession(); }
这样做可以避免在代码中自己实现策略模式与IOC,NHibernate已经帮我们做好了。
修改hibernate.cfg.xml配置文件,加入一个属性<property name="current_session_context_class">web</property>,修改这个属性就可以修改Session策略。
也可以定义在代码中。
sessionFactory = new Configuration().Configure() .SetProperty("current_session_context_class", currentSessionContextClass.ToString()) .BuildSessionFactory(); /// <summary> /// 当前Session上下文模式 /// 特定环境下,当前Session保存在特定的上下文中 /// </summary> public enum CurrentSessionContextClass { managed_web, call, thread_static, web }
现在,我们就可以在web应用程序中将参数设置为web,而单元测试中将参数设置为thread_static。
void Application_Start(object sender, EventArgs e) { // 在应用程序启动时运行的代码 NHibernateHelper.Instance(CurrentSessionContextClass.web); }
ISession session; [SetUp] public void Setup() { NHibernateHelper.Instance(CurrentSessionContextClass.thread_static); session = NHibernateHelper.GetCurrentSession(); }
下载:
最后分享一个相关的NHibernateHelper文件