Dlaczego Nhibernate dzieli sesję na wiele żądań w mojej aplikacji MVC?

Mamy projekt MVC, który konstruuje zależności NHibernate poprzez StructureMap w ten sposób

var sessionFactory = ConnectionRegistry.CreateSessionFactory<NHibernate.Context.WebSessionContext>();
For<ISessionFactory>().Singleton().Use(sessionFactory);
For<INHibernateSessionManager>().Singleton().Use<NHibernateWebSessionManager>();

Fabryka ConnectionRegistry.CreateSessionFactory wygląda tak

public static ISessionFactory CreateSessionFactory<T>() where T : ICurrentSessionContext
        {
            if (_sessionFactory == null)
            {
                lock (_SyncLock)
                {
                    if (_sessionFactory == null)
                    {
                        var cfg = Fluently.Configure()
                            .Database(MsSqlConfiguration.MsSql2005.ConnectionString(DataFactory.ConnectionString))
                            .CurrentSessionContext<T>()
                            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<IInstanceFactory>())
                            .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 wygląda tak

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;
        }

W Application_EndRequest to robimy

ObjectFactory.GetInstance<INHibernateSessionManager>().CloseSession();
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();

Nasze kontrolery są agnostyczne i trwałe wywołują zapytania do dostawców modelu lub procesorów poleceń, do których wprowadzono menedżer sesji i zarządzają własnymi transakcjami.

Na przykład:

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

    //redirect
}

W CommandProcessor:

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

Mamy również atrybut ActionFilter, który rejestruje dostęp do każdej akcji kontrolera.

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

SessionLogger zarządza również własnymi transakcjami z wprowadzonego menedżera sesji

public void LogUserActionSummary(int sessionId, string userActionTypeDescription)
        {

            using (var tx = _session.BeginTransaction())
            {
                //get activity summary
                _session.Save(userActivitySummary);
                tx.Commit();
            }
        }

Wszystko to działa dobrze, dopóki nie mam dwóch przeglądarek uzyskujących dostęp do aplikacji. W tym scenariuszu występują błędy przerywane, ponieważ sesja (NHibernate) jest zamknięta. NHProfiler pokazuje instrukcje SQL utworzone zarówno z metod CommandProcessor, jak i metod SessionLogger z obu sesji przeglądarki w ramach tej samej transakcji.

Jak może to nastąpić, biorąc pod uwagę zakres WebSessionContext? Próbowałem także ustawić zakres sesji sessionManager na HybridHttpOrThreadLocalScoped za pomocą structMap.

questionAnswers(1)

yourAnswerToTheQuestion