NHibernate 配置文件的处理和使用多数据库的多层架构思路(补充部分一)
补充的原因:针对 session 的管理有点头疼!
《NHibernate 配置文件的处理和使用多数据库的多层架构思路》(一)中底层的思路只适合单线程的情况!比如这类简单的 MIS 系统:一个或是多个客户端安装在各自的机器上连接同一个或是多个数据库。因为针对一个数据库仅缓存了一个 session 对象。多线程很显然就不适用了!比如针对ASP.NET, .Net Remoting,上面的结构就不适用了!就算是简单的 Mis 系统,也尽量将 session 用完就关掉(Disconnect()),需要的时候重新打开(Reconnect()),由session的属性IsConnected 来判断。我在(一)中的底层的EntityControl类与session相关的各个方法中没有这样做。但是这个补充部分完成了的!
另外,有时可能会遇见这个异常:a different object with the same identifier value was already associated with the session: 2, of class: ..... .这是因为 session 是个全局的一直保存着,如果先用 AddEntity(obj) , 它里面就含了一个对象 obj,后来又使用了 UpdateEntity(obj1, obj1.Id); 而obj1.Id和 obj.Id一样且obj和obj1不是指向同一个对象。解决方法是:要么保证obj1和obj指向同一个对象(就是先从session取出 obj,然后给它的属性赋值),要么在两步操作的中间使用session.Clear()。
如果要解决多线程问题怎么办?主要问题在于管理 session 对象上!
虽然可以针对(一)中 EntityControl 类中在每个方法中都声明 session 的局部变量,然后由 SessionFactory 类提供创建新的 session 对象,这种解决方法可以构成通杀,单线程,多线程都适用,但是效率低下,不适合我们的需求。如果我们解决了session 对象的管理,只需要在前面的思路中对 SessionFactory 类重写 public ISession OpenSession(); 方法即可。其他的变化均 session 对象的管理相关。也就是说,只有底层中只有一两个类有变化,其他层不发生任何变化,不影响多层架构思路.
具体情况具体对待了!(其实我都不想写下去了,因为有很多不错的文章,比如《NHibernate的Session管理》已经把我马上要列举的两类情况都包含进去了!只是由于以前写(一)、(二)、(三)这三部分,我还是有责任把没有说清楚的地方解释清楚,更重要的是给自己做个笔记以便自己查阅,有错误的地方也可以晾出来让大家给指出来。另外我和他们一小点不同的地方是我针对于多个数据库操作的。)
处理两种具体情况:
第一种情况:ASP.NET。
它的特点是为每个用户提供一个 session 是最好的。显然又不可能为每个用户一直保存独立 的 session。那么为请求服务的每个活动线程提供一个 session,这样也不够好,因为每个请求可以涉及到多个线程,大家用的都是同一个 session,何必为涉及到的多个线程提供单独的 session 呢?现在情况就明朗,为每个请求提供一个 session, 请求结束时 session 释放。
网上已经有很多文章解决这个问题了。我只简单提一下:在每次 request 请求模式,在第一次使用时创建一个 session(不是在context.。BeginRequest 时就创建session),request 请求模式结束时(context.EndRequest)释放session 。
(1) 配置 web.config
<httpModules>
<add name="NHibernateModule" type="NHibernate.Helper.Module, NHibernate.Helper" />
</httpModules>
(2)创建请求模式结束需要使用的类。
// 参考地址: http://blogs.intesoft.net/simon/articles/16.aspx
// -----------------------------------------------------------------------------------------
using System;
using System.Web;
namespace NHibernate.Helper
{
public sealed class Module : IHttpModule
{
public void Init(HttpApplication context)
{
context.EndRequest += new EventHandler(EndRequest);
}
public void Dispose() { }
public void EndRequest(Object sender, EventArgs e)
{
SessionFactory.CloseSession();
}
}
}
(3)创建包含所有操作的类。省略了一些代码。这个东西很久以前就有了,所以我都不好意思写太多。
// 参考地址: http://blogs.intesoft.net/simon/articles/16.aspx
// -----------------------------------------------------------------------------------------
using System;
using System.Web;
using System.Configuration;
using NHibernate;
using NHibernate.Cfg;
namespace NHibernate.Helper
{
public sealed class SessionFactory
{
private static readonly Configuration configuration = new Configuration();
private static readonly ISession session = sessionFactory.OpenSession();
private static readonly ISessionFactory sessionFactory = configuration.Configure( HttpContext.Current.Request.MapPath(ConfigurationSettings.AppSettings["nhibernate.config"]) ).BuildSessionFactory();
[CLSCompliant(false)]
public static ISession Session
{
get
{
ISession session;
if (HttpContext.Current == null)
{
session =SessionFactory.session;
}
else
{
if (HttpContext.Current.Items.Contains(sessionKey))
{
session = (ISession)HttpContext.Current.Items[sessionKey];
}
else
{
session = SessionFactory.sessionFactory.OpenSession();
HttpContext.Current.Items[sessionKey] = session;
}
}
return session;
}
}
[CLSCompliant(false)]
public static void CloseSession()
{
if (HttpContext.Current == null)
{
SessionFactory.session.Close();
}
else
{
if (HttpContext.Current.Items.Contains(sessionKey))
{
ISession session = (ISession)HttpContext.Current.Items[sessionKey];
session.Close();
HttpContext.Current.Items.Remove(sessionKey);
}
}
}
}