为什么Nhibernate在我的MVC应用程序中跨多个请求共享会话?

我们有一个MVC项目,通过StructureMap构建NHibernate依赖项

var sessionFactory = ConnectionRegistry.CreateSessionFactory(); For().Singleton().Use(sessionFactory); For().Singleton().Use(); 

ConnectionRegistry.CreateSessionFactory看起来像这样

 public static ISessionFactory CreateSessionFactory() where T : ICurrentSessionContext { if (_sessionFactory == null) { lock (_SyncLock) { if (_sessionFactory == null) { var cfg = Fluently.Configure() .Database(MsSqlConfiguration.MsSql2005.ConnectionString(DataFactory.ConnectionString)) .CurrentSessionContext() .Mappings(m => m.FluentMappings.AddFromAssemblyOf()) .ExposeConfiguration(c => c.SetProperty("generate_statistics", "true")) .ExposeConfiguration(c => c.SetProperty("sql_exception_converter", typeof(SqlServerExceptionConverter).AssemblyQualifiedName)); try { _sessionFactory = cfg.BuildSessionFactory(); } catch (Exception ex) { Debug.Write("Error loading Fluent Mappings: " + ex); throw; } } } } return _sessionFactory; } 

NHibernateWebSessionManager看起来像这样

 public ISession Session { get { return OpenSession(); } } public ISession OpenSession() { if(CurrentSessionContext.HasBind(SessionFactory)) _currentSession = SessionFactory.GetCurrentSession(); else { _currentSession = SessionFactory.OpenSession(); CurrentSessionContext.Bind(_currentSession); } return _currentSession; } public void CloseSession() { if (_currentSession == null) return; if (!CurrentSessionContext.HasBind(SessionFactory)) return; _currentSession = CurrentSessionContext.Unbind(SessionFactory); _currentSession.Dispose(); _currentSession = null; } 

在Application_EndRequest中,我们这样做

 ObjectFactory.GetInstance().CloseSession(); ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects(); 

我们的控制器是持久性不可知的,并且操作调用查询注入了sessionManager的模型提供程序或命令处理器并管理它们自己的事务。

例如:

 public ActionResult EditDetails(SiteDetailsEditViewModel model) { _commandProcessor.Process(new SiteEditCommand { //mappings } //redirect } 

在CommandProcessor中:

 public void Process(SiteEditCommand command) { using (var tran = _session.BeginTransaction()) { var site = _session.Get(command.Id); site.SiteName = command.Name; //more mappings tran.Commit(); } } 

我们还有一个ActionFilter属性,用于记录对每个控制器操作的访问。

 public void OnActionExecuted(ActionExecutedContext filterContext) { SessionLogger.LogUserActionSummary(session, _userActionType); } 

SessionLogger还从注入的SessionManager管理自己的事务

 public void LogUserActionSummary(int sessionId, string userActionTypeDescription) { using (var tx = _session.BeginTransaction()) { //get activity summary _session.Save(userActivitySummary); tx.Commit(); } } 

所有这一切都正常,直到我有两个浏览器访问该应用程序。 在这种情况下,会抛出间歇性错误,因为(NHibernate)会话已关闭。 NHProfiler显示从同一事务中的两个浏览器会话中的CommandProcessor方法和SessionLogger方法创建的SQL语句。

在WebSessionContext范围内如何发生这种情况? 我还尝试通过structureMap将sessionManager的范围设置为HybridHttpOrThreadLocalScoped。

问题是单身人士的组合:

 For().Singleton().Use(); 

引用来自不同范围的对象(webrequest上下文)

 _currentSession = SessionFactory.GetCurrentSession(); 

这个canot在multithreading环境中正常工作(如两个并发浏览器访问它时所述)。 第一个请求可能已强制设置字段_currentSession ,然后(有一段时间)甚至用于第二个。 第一个Application_EndRequest将关闭它…持久的将重新创建它…

在依赖NHibernate范围时,请完全遵循:

 return SessionFactory.GetCurrentSession(); // inside is the scope handled 

SessionManager.Open()

 public ISession OpenSession() { if(CurrentSessionContext.HasBind(SessionFactory)) { return SessionFactory.GetCurrentSession(); } // else var session = SessionFactory.OpenSession(); NHibernate.Context.CurrentSessionContext.Bind(session); return session; } 

那么即使单例返回正确的实例也应该有效。 但对于SessionManager,无论如何我都会使用HybridHttpOrThreadLocalScoped

 For() .HybridHttpOrThreadLocalScoped() .Use();