Создание сеанса nhibernate для каждого веб-запроса с помощью Castle.Facility.AutoTx и Castle.Facility.NHibernate
Я использую Castle Windors, и это AutoTx и NHibernate Facility от haf. В конечном итоге я хочу воспользоваться преимуществами простоты использования атрибута Transaction, предоставляемого AutoTx. (Проект ASP.NET MVC 4).
Я использую Castle.Facilities.NHibernate.ISessionManager для управления своими сеансами PerWebRequest. Я установил Виндзорский Установщик так:
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.AddFacility<AutoTxFacility>();
container.Register(Component.For<INHibernateInstaller>().ImplementedBy<NHibernateInstaller>().LifeStyle.Singleton);
container.AddFacility<NHibernateFacility>(f => f.DefaultLifeStyle = DefaultSessionLifeStyleOption.SessionPerWebRequest);
container.Install(FromAssembly.Containing<PersonRepository>());
}
Я использую DefaultLifeStyle SessionPerWebRequest, который, как я ожидаю, сделает именно это, предоставит мне сеанс, который длится весь веб-запрос, так что все вызовы OpenSession в SessionManager внутри одного запроса используют один и тот же сеанс. Я проверяю это с помощью следующего кода:
public class HomeController : Controller
{
private readonly ISessionManager _sessionManager;
public HomeController(ISessionManager sessionManager)
{
_sessionManager = sessionManager;
}
public ActionResult Index()
{
using (var session1 = _sessionManager.OpenSession())
{
var person = session1.Get<Person>(1);
using (var session2 = _sessionManager.OpenSession())
{
var person2 = session2.Get<Person>(1);
}
}
return View();
}
}
и проверка журнала, чтобы увидеть идентификатор каждого созданного сеанса. Идентификатор всегда отличается. например
05/01/2013 11:27:39.109 DEBUG 9 NHibernate.Impl.SessionImpl - [session-id=c1ba248a-14ba-4468-a20c-d6114b7dac61] opened session at timestamp: 634929820591, for session factory: [/ea869bb12b4d4e51b9f431a4f9c9d9fa]
05/01/2013 11:30:36.383 DEBUG 9 NHibernate.Impl.SessionImpl - [session-id=72481180-625d-4085-98e9-929e3fd93e8a] opened session at timestamp: 634929822363, for session factory: [/ea869bb12b4d4e51b9f431a4f9c9d9fa]
Стоит отметить, что я ничего не добавил в web.config в качестве обработчиков. Нужно ли мне? (Я не видел никакой документации, предлагающей это в вики-сайте NHib Facility) Мои ожидания, что одна и та же сессия всегда будет возвращаться неверно.
Я просмотрел исходный код объекта и не понимаю, как создается экземпляр сеанса на веб-запрос и как многократные вызовы OpenSession приводят к одному и тому же сеансу в одном и том же веб-запросе.
Ниже описано, как SessionManager зарегистрирован в Windsor:
Component.For<ISessionManager>().Instance(new SessionManager(() =>
{
var factory = Kernel.Resolve<ISessionFactory>(x.Instance.SessionFactoryKey);
var s = x.Instance.Interceptor.Do(y => factory.OpenSession(y)).OrDefault(factory.OpenSession());
s.FlushMode = flushMode;
return s;
}))
.Named(x.Instance.SessionFactoryKey + SessionManagerSuffix)
.LifeStyle.Singleton
ISession зарегистрирован в Windsor с использованием следующего
private IRegistration RegisterSession(Data x, uint index)
{
Contract.Requires(index < 3,
"there are only three supported lifestyles; per transaction, per web request and transient");
Contract.Requires(x != null);
Contract.Ensures(Contract.Result<IRegistration>() != null);
return GetLifeStyle(
Component.For<ISession>()
.UsingFactoryMethod((k, c) =>
{
var factory = k.Resolve<ISessionFactory>(x.Instance.SessionFactoryKey);
var s = x.Instance.Interceptor.Do(y => factory.OpenSession(y)).OrDefault(factory.OpenSession());
s.FlushMode = flushMode;
logger.DebugFormat("resolved session component named '{0}'", c.Handler.ComponentModel.Name);
return s;
}), index, x.Instance.SessionFactoryKey);
}
private ComponentRegistration<T> GetLifeStyle<T>(ComponentRegistration<T> registration, uint index, string baseName)
where T : class
{
Contract.Requires(index < 3,
"there are only three supported lifestyles; per transaction, per web request and transient");
Contract.Ensures(Contract.Result<ComponentRegistration<T>>() != null);
switch (defaultLifeStyle)
{
case DefaultSessionLifeStyleOption.SessionPerTransaction:
if (index == 0)
return registration.Named(baseName + SessionPerTxSuffix).LifeStyle.PerTopTransaction();
if (index == 1)
return registration.Named(baseName + SessionPWRSuffix).LifeStyle.PerWebRequest;
if (index == 2)
return registration.Named(baseName + SessionTransientSuffix).LifeStyle.Transient;
goto default;
case DefaultSessionLifeStyleOption.SessionPerWebRequest:
if (index == 0)
return registration.Named(baseName + SessionPWRSuffix).LifeStyle.PerWebRequest;
if (index == 1)
return registration.Named(baseName + SessionPerTxSuffix).LifeStyle.PerTopTransaction();
if (index == 2)
return registration.Named(baseName + SessionTransientSuffix).LifeStyle.Transient;
goto default;
case DefaultSessionLifeStyleOption.SessionTransient:
if (index == 0)
return registration.Named(baseName + SessionTransientSuffix).LifeStyle.Transient;
if (index == 1)
return registration.Named(baseName + SessionPerTxSuffix).LifeStyle.PerTopTransaction();
if (index == 2)
return registration.Named(baseName + SessionPWRSuffix).LifeStyle.PerWebRequest;
goto default;
default:
throw new FacilityException("invalid index passed to GetLifeStyle<T> - please file a bug report");
}
}
который регистрирует ISession как PerWebRequest, но я не вижу нигде в коде, где эта именованная регистрация извлекается, когда требуется сеанс?
Любая помощь в том, что мне нужно сделать, чтобы получить сеанс для каждого веб-запроса, приветствуется.
ОБНОВЛЕНИЕ Я решил просто заменить функцию кода, передаваемую в конструктор SessionManager, кодом, который захватывает ISession из контейнера, а не использует фабрику. Прекрасно работает для того, что я хочу, включая завершение транзакций и открытие только одного сеанса на веб-запрос, или переходный процесс и т. Д.
Component.For<ISessionManager>().Instance(new SessionManager(() =>
{
var s = Kernel.Resolve<ISession>();
s.FlushMode = flushMode;
return s;
}))
//Component.For<ISessionManager>().Instance(new SessionManager(() =>
//{
// var factory = Kernel.Resolve<ISessionFactory>(x.Instance.SessionFactoryKey);
// var s = x.Instance.Interceptor.Do(y => factory.OpenSession(y)).OrDefault(factory.OpenSession());
// s.FlushMode = flushMode;
// return s;
//}))
.Named(x.Instance.SessionFactoryKey + SessionManagerSuffix)
.LifeStyle.Singleton
Kernel.Resolve () Я ожидаю, будет захватить первый зарегистрированный сервис в контейнере. Это будет то, что я установил для образа жизни.