Почему Nhibernate разделяет сеанс между несколькими запросами в моем приложении MVC?

У нас есть проект MVC, который создает зависимости NHibernate через StructureMap, как это

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

ConnectionRegistry.CreateSessionFactory выглядит следующим образом

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 выглядит следующим образом

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<INHibernateSessionManager>().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<DeliveryPoint>(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 показывает операторы SQL, созданные как из методов CommandProcessor, так и из методов SessionLogger из обоих сеансов браузера в рамках одной транзакции.

Как это может произойти с учетом области действия WebSessionContext? Я также попытался установить область видимости sessionManager в HybridHttpOrThreadLocalScoped через StructureMap.

Ответы на вопрос(1)

Ваш ответ на вопрос