我的ASP.NET MVC 4项目使用NHibernate(在存储库后面)和Castle Windsor,使用AutoTx和NHibernate工具。我按照haf写的指南,我可以创建和阅读对象。
My PersistenceInstaller看起来像这样
public class PersistenceInstaller : IWindsorInstaller
{
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);
}
}
NHibernateInstaller直接来自NHib Facility Quickstart。
我在我的基础存储库中使用ISessionManager ...
protected ISession Session
{
get
{
return _sessionManager.OpenSession();
}
}
public virtual T Commit(T entity)
{
Session.SaveOrUpdate(entity);
return entity;
}
最后,我的应用程序代码导致问题:
[HttpPost]
[ValidateAntiForgeryToken]
[Transaction]
public ActionResult Maintain(PrescriberMaintainViewModel viewModel)
{
if (ModelState.IsValid)
{
var prescriber = UserRepository.GetPrescriber(User.Identity.Name);
//var prescriber = new Prescriber { DateJoined = DateTime.Today, Username = "Test" };
prescriber.SecurityQuestion = viewModel.SecurityQuestion;
prescriber.SecurityAnswer = viewModel.SecurityAnswer;
prescriber.EmailAddress = viewModel.Email;
prescriber.FirstName = viewModel.FirstName;
prescriber.LastName = viewModel.LastName;
prescriber.Address = new Address
{
Address1 = viewModel.AddressLine1,
Address2 = viewModel.AddressLine2,
Address3 = viewModel.AddressLine3,
Suburb = viewModel.Suburb,
State = viewModel.State,
Postcode = viewModel.Postcode,
Country = string.Empty
};
prescriber.MobileNumber = viewModel.MobileNumber;
prescriber.PhoneNumber = viewModel.PhoneNumber;
prescriber.DateOfBirth = viewModel.DateOfBirth;
prescriber.AHPRANumber = viewModel.AhpraNumber;
prescriber.ClinicName = viewModel.ClinicName;
prescriber.ClinicWebUrl = viewModel.ClinicWebUrl;
prescriber.Qualifications = viewModel.Qualifications;
prescriber.JobTitle = viewModel.JobTitle;
UserRepository.Commit(prescriber);
}
return View(viewModel);
}
以上代码将保存一个新的处方者(通过取消注释注释掉的行等进行测试)。
我正在使用NHProf并确认没有将sql发送到数据库以进行更新。我可以看到正在执行的读取,但就是这样。
在我看来,NHibernate不会将实体识别为被更改,因此不会生成sql。或者可能没有提交交易?
我已经在网上搜了几个小时,现在正试图解决这个问题,最后的绝望行为已经发布在SO上了。有任何想法吗? :)
哦,在NHProf中我看到三个Sessions(1个用于来自repo的GetPrescriber调用,一个我假设用于更新(没有sql) - 以及一个用于基类上的actionfilter中的一些操作)。我还得到关于隐式事务使用的警报。这让我感到困惑,因为我认为我正在做我需要的所有事情 - 使用AutoTx和Transaction属性。我还期望每个webrequest只有一个会话,根据我的Windsor配置。
更新:似乎在花了一天阅读NHibernateFacility和AutoTx Facility的源代码进行自动事务后,AutoTx没有在我的INHibernateInstaller实现上设置拦截器。看来这意味着每当SessionManager调用OpenSession时,它都会调用没有参数的默认版本,而不是接受拦截器的版本。内部AutoTxFacility使用windsor注册TransactionInterceptor,以便可以通过Windsor利用AutoTx的TransactionalComponentInspector将其添加到我的INHibernateInstaller具体的Interceptor上
答案 0 :(得分:2)
对我而言,它似乎为每次调用存储库创建会话。会话应该涵盖整个业务运营。它应该在开始时打开并在最后提交和处理。
此代码中还有其他奇怪的内容。
答案 1 :(得分:1)
上面的代码段中有一个最可能是“非预期”的部分。并通过NHProf观察证明
哦,在NHProf我看到三个Sessions(1个用于GetPrescriber呼叫 从repo,我假设更新(没有sql) - 和一个 我的actionfilter中对基类的一些动作。)
致电 OpenSession()
会触发创建新会话实例。
protected ISession Session
{
get { return _sessionManager.OpenSession(); }
}
因此,每当代码访问Session
属性时,后面都会创建新会话实例(一次又一次)。 get的一个会话,udpate的一个会话,过滤器的一个会话......
正如我们在这里看到的那样,SessionManager.OpenSession()
返回的会话必须用于整个范围(工作单元,Web请求......)
http://docs.castleproject.org/Windsor.NHibernate-Facility.ashx
我们需要的语法,用于创建一个会话(首次访问时)并重用它直到范围的enf(然后再正确关闭它,提交或回滚事务......)。无论如何,现在首先要以这种方式改变Session
属性:
ISession _session;
protected ISession Session
{
get
{
if (_session == null)
{
_session = sessionFactory.OpenSession();
}
return _session;
}
}
答案 2 :(得分:0)
昨天花了一整天的时间在github上搜索AutoTx和NHibernate工具并无处可去,我开始了一个干净的项目,试图复制问题。不幸的是,复制一切正常!我在源代码上运行了Update-Package并关闭了新版本的Castle.Transactions,我运行正常。我对自己的代码进行了一些小调整。那就是删除UserRepository.Commit行。
我不需要修改我打开会话的方式。这是由SessionManager实例处理的。随着Castle.Transactions的更新,正在识别Transaction属性并正在创建一个事务(在NHProf中没有更多警报证明)。