在NHibernate中,ISessionFactory是线程安全的,对应一个数据库。它是生成ISession的工厂。而ISession是线程不安全的。
创建一个ISessionFactory需要消耗比较多的资源。因此,我们只在程序初始化的时候创建一次,以后就一直使用这个ISessionFactory。
而ISession的创建只消耗很少的资源。因此我们可以随意创建。
一、原始单例模式封装的ISessionFactory
因此,对于ISessionFactory,我们使用饿汉单例模式实现它。
原始饿汉单例模式封装ISessionFactory实例:
//密封类
public sealed class NSession
{
//私有、静态、只读
private static readonly ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
//私有构造函数,防止new
private NSession()
{
}
public static ISessionFactory GetSessionFactory()
{
return sessionFactory;
}
}
OK,对于ISessionFactory,以上代码就能够保证,整个程序只有一个SessionFactory的实例了。
虽然,上面的代码已经使用单例模式实现了SessionFactory只能保证只有一个实例。但是,实际上我们能够进一步封装,实现管理到 ISession。因为在我们的程序当中,我们实际上使用的是ISession。而我们要得到ISession对象,每次都要在代码里调用
ISession iSession = NSession.GetSessionFactory().OpenSession();
这样的代码来获得ISession对象。我们何不干脆封装到ISession呢?
二、利用HttpContext绑定ISession
上面说到,我们实际上要用到的是ISession对象而不是ISessionFactory对象。所以,我们干脆封装到ISession,实现更简单的调用。
我们,先来看看以下代码的问题:
//密封类
public sealed class NSession
{
//私有、静态、只读
private static readonly ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
//私有构造函数,防止new
private NSession()
{
}
//返回ISession
public static ISession GetSession()
{
return sessionFactory.OpenSession();
}
}
测试代码:
public PersonModel GetPerson(int Id)
{
ISession iSession1 = NSession.GetSession();
ISession iSession2 = NSession.GetSession();
HttpContext.Current.Response.Write(object.ReferenceEquals(iSession1, iSession2)); //输出 False,这是两个ISession对象
return iSession1.Get<PersonModel>(Id);
}
我们看到,假若我们想上面那种封装方法,只要调用了一次GetSession()方法,就会生成一个新的ISession对象,虽然这样 ISession占用的资源不多,但总感觉有多少浪费,我们何不将ISession绑定到HttpContext中,实现对于一次Http请求,只创建一 个ISession呢?
//密封类
public sealed class NSession
{
//私有、静态、只读
private static readonly ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
//私有构造函数,防止new
private NSession()
{
}
//获取ISession
public static ISession GetSession()
{
HttpContext context = HttpContext.Current;
ISession currentSession = context.Items["ISession"] as ISession;
//如果对于本次请求的HttpContext里还没有ISession对象,才OpenSession(),同时存入HttpContext中,用于下次判断和Close()
if (currentSession == null)
{
currentSession = sessionFactory.OpenSession();
context.Items["ISession"] = currentSession;
}
return currentSession;
}
//关闭ISession
public static void CloseSession()
{
HttpContext context = HttpContext.Current;
ISession currentSession = context.Items["ISession"] as ISession;
//如果对于本次请求还没有创建ISession对象,那就用不着关闭了
if (currentSession != null)
{
currentSession.Close();
context.Items.Remove("ISession");
}
}
//关闭SessionFactory
public static void CloseSessionFactory()
{
if (sessionFactory != null)
{
sessionFactory.Close();
}
}
}
我们再来测试下:
public PersonModel GetPerson(int Id)
{
ISession iSession1 = NSession.GetSession();
ISession iSession2 = NSession.GetSession();
HttpContext.Current.Response.Write(object.ReferenceEquals(iSession1, iSession2)); //输出 True,这是两个ISession对象
return iSession1.Get<PersonModel>(Id);
}
这次是输出True了。说明,这两个是同一个对象。
以上代码就实现了HttpContext与ISession对象挂钩,对于一次HttpContext只创建一个ISession。当请求响应 完毕,HttpContext里面的ISession就自动释放掉了。对于在请求响应未完毕之前,该ISession都一直处于打开状态(例如渲染视图 时),不影响操作。
以上代码依赖于HttpContext,因此只适合于Web程序。
三、非Web程序中封装ISession
而对于在WinForm或控制台项目中,由于程序是跑在客户端上,一个客户端电脑,哪怕你Open好几百个ISession都没什么问题,只是要管理好ISessionFactory,因为ISessionFactory还是比较占用资源的。
对于非Web程序中的NHibernate帮助类实现如下:
public sealed class NSession
{
private static readonly ISessionFactory sessionFactory;
static NSession()
{
sessionFactory = new Configuration().Configure().BuildSessionFactory();
}
public static ISession GetSession()
{
return sessionFactory.OpenSession();
}
public static void CloseSession(ISession currentSession)
{
if (currentSession != null)
{
currentSession.Close();
}
}
public static void CloseSessionFactory()
{
if (sessionFactory != null)
{
sessionFactory.Close();
}
}
}