NHibernate和奇怪的“Session is Closed!”错误

注意:现在我输入了这个,我不得不为超长问题道歉,但是,我认为这里提供的所有代码和信息在某种程度上都是相关的。


好吧,我在ASP.NET webforms应用程序的随机点上得到奇怪的“Session Is Closed”错误。 然而,今天,它终于在同一个地方一次又一次地发生。 我几乎可以确定在我的代码中没有任何处理或关闭会话,因为使用的代码位远远超出了所有其他代码,如下所示。

我也使用ninject作为我的IOC,这可能/可能不重要。

好的,首先是我的SessionFactoryProviderSessionProvider类:


SessionFactoryProvider

 public class SessionFactoryProvider : IDisposable { ISessionFactory sessionFactory; public ISessionFactory GetSessionFactory() { if (sessionFactory == null) sessionFactory = Fluently.Configure() .Database( MsSqlConfiguration.MsSql2005.ConnectionString(p => p.FromConnectionStringWithKey("QoiSqlConnection"))) .Mappings(m => m.FluentMappings.AddFromAssemblyOf()) .BuildSessionFactory(); return sessionFactory; } public void Dispose() { if (sessionFactory != null) sessionFactory.Dispose(); } } 

SessionProvider

 public class SessionProvider : IDisposable { ISessionFactory sessionFactory; ISession session; public SessionProvider(SessionFactoryProvider sessionFactoryProvider) { this.sessionFactory = sessionFactoryProvider.GetSessionFactory(); } public ISession GetCurrentSession() { if (session == null) session = sessionFactory.OpenSession(); return session; } public void Dispose() { if (session != null) { session.Dispose(); } } } 

这两个类与Ninject连接如下:

NHibernateModule

 public class NHibernateModule : StandardModule { public override void Load() { Bind().ToSelf().Using(); Bind().ToSelf().Using(); } } 

并且据我所知,可以按预期工作。

现在我的BaseDao类:


BaseDao

 public class BaseDao : IDao where T : EntityBase { private SessionProvider sessionManager; protected ISession session { get { return sessionManager.GetCurrentSession(); } } public BaseDao(SessionProvider sessionManager) { this.sessionManager = sessionManager; } public T GetBy(int id) { return session.Get(id); } public void Save(T item) { using (var transaction = session.BeginTransaction()) { session.SaveOrUpdate(item); transaction.Commit(); } } public void Delete(T item) { using (var transaction = session.BeginTransaction()) { session.Delete(item); transaction.Commit(); } } public IList GetAll() { return session.CreateCriteria().List(); } public IQueryable Query() { return session.Linq(); } } 

哪个绑定在Ninject中就像这样:


DaoModule

 public class DaoModule : StandardModule { public override void Load() { Bind(typeof(IDao)).To(typeof(BaseDao)) .Using(); } } 

现在导致这种情况的Web请求是在我保存对象时,直到我今天进行了一些模型更改才发生,但是我的模型的更改并没有改变数据访问代码。 虽然它改变了一些NHibernate映射(如果有人有兴趣,我也可以发布这些映射)

据我所知, BaseDao.Get被调用然后BaseDao.Get被调用然后BaseDao.Save被调用。

这是Save()第三次调用

 using (var transaction = session.BeginTransaction()) 

失败,“会话已关闭!” 或者说是例外:

 Session is closed! Object name: 'ISession'. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. Exception Details: System.ObjectDisposedException: Session is closed! Object name: 'ISession'. 

事实上,在调试器上显示第三次从SessionProvider请求会话时,它确实已关闭且未连接。

我已经validation在我的SessionFactoryProvider和我的SessionProvider上的Dispose在请求结束时被调用,而不是在我的Dao上进行Save调用之前。

所以现在我有点卡住了。 想到一些事情。

  • 我做了什么明显的错误吗?
  • NHibernate会在没有我要求的情况下关闭会话吗?
  • 关于我可能会做什么的任何变通办法或想法?

提前致谢

ASP.NET是multithreading的,因此对ISession的访问必须是线程安全的。 假设您正在使用每个请求的会话,最简单的方法是使用NHibernate内置的上下文会话处理。

首先配置NHibernate以使用Web会话上下文类:

 sessionFactory = Fluently.Configure() .Database( MsSqlConfiguration.MsSql2005.ConnectionString(p => p.FromConnectionStringWithKey("QoiSqlConnection"))) .Mappings(m => m.FluentMappings.AddFromAssemblyOf()) .ExposeConfiguration(x => x.SetProperty("current_session_context_class", "web") .BuildSessionFactory(); 

然后使用ISessionFactory.GetCurrentSession()获取现有会话,或者如果不存在则将新会话绑定到工厂。 下面我将剪切+粘贴我的代码以打开和关闭会话。

  public ISession GetContextSession() { var factory = GetFactory(); // GetFactory returns an ISessionFactory in my helper class ISession session; if (CurrentSessionContext.HasBind(factory)) { session = factory.GetCurrentSession(); } else { session = factory.OpenSession(); CurrentSessionContext.Bind(session); } return session; } public void EndContextSession() { var factory = GetFactory(); var session = CurrentSessionContext.Unbind(factory); if (session != null && session.IsOpen) { try { if (session.Transaction != null && session.Transaction.IsActive) { session.Transaction.Rollback(); throw new Exception("Rolling back uncommited NHibernate transaction."); } session.Flush(); } catch (Exception ex) { log.Error("SessionKey.EndContextSession", ex); throw; } finally { session.Close(); session.Dispose(); } } } 

在为我正在处理的Web API项目运行集成测试时,我间歇性地遇到了Session Closed / ObjectDisposedExceptions。

这里没有任何提示解决了它,所以我构建了一个NHibernate的调试版本以了解更多信息,当控制器的Get函数返回IEnumerable响应对象时,我看到它正在执行linq查询。

事实certificate,我们的存储库正在执行linq查询并返回域对象的IEnumerable ,但从不调用ToList()来强制查询的评估。 该服务依次返回IEnumerable ,并且Controller将枚举包装为响应对象并将其返回。

在返回期间,linq查询最终被执行,但NHibernate会话在某个时间点暂时关闭,因此它抛出exception。 解决方案是始终确保在返回服务之前在使用会话块中的存储库中调用.ToList()

我建议你在SessionImpl.Close / SessionImpl.Dispose上设置一个断点 ,看看是谁通过堆栈跟踪调用它。 您也可以为自己构建一个NH的调试版本并执行相同的操作。