4

このように、StructureMap を介して NHibernate の依存関係を構築する MVC プロジェクトがあります。

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

2 つのブラウザーがアプリにアクセスするまで、これはすべて正常に機能します。このシナリオでは、(NHibernate) セッションが閉じられているため、断続的なエラーがスローされます。NHProfiler は、同じトランザクション内の両方のブラウザー セッションの CommandProcessor メソッドと SessionLogger メソッドの両方から作成された SQL ステートメントを示します。

WebSessionContext スコープを考えると、これはどのように発生するのでしょうか? また、sessionManager のスコープを structureMap を介して HybridHttpOrThreadLocalScoped に設定しようとしました。

4

1 に答える 1

2

問題はシングルトンの組み合わせです:

For<INHibernateSessionManager>().Singleton().Use<NHibernateWebSessionManager>();

異なるスコープ (webrequest コンテキスト) からのオブジェクトへの参照を持つ

_currentSession = SessionFactory.GetCurrentSession();

これは、マルチスレッド環境では適切に機能しません (2 つのブラウザーが同時にアクセスする場合で述べたように)。最初のリクエストはすでに field の設定を強制できますが_currentSession、これは (しばらくの間) 2 番目のリクエストにも使用されます。最初の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;
}

次に、正しいインスタンスを返すシングルトンでも機能するはずです。しかし、SessionManagerHybridHttpOrThreadLocalScopedの場合はとにかく使用します。

For<INHibernateSessionManager>()
  .HybridHttpOrThreadLocalScoped()
  .Use<NHibernateWebSessionManager>();
于 2012-12-03T10:31:25.230 に答える