このように、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 に設定しようとしました。