NHibernate不会持续更改我的对象

时间:2012-12-04 05:54:18

标签: nhibernate asp.net-mvc-4 castle-windsor

我的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上

AutoTxFacility source on github

3 个答案:

答案 0 :(得分:2)

对我而言,它似乎为每次调用存储库创建会话。会话应该涵盖整个业务运营。它应该在开始时打开并在最后提交和处理。

此代码中还有其他奇怪的内容。

  • 提交是与SaveOrUpdate完全不同的概念。
  • 无论如何,您无需告诉NH存储更改。您不需要为会话中已有的对象调用session.Save。无论如何它们都存储起来。您只需在添加新对象时调用session.Save。
  • 确保您为整个业务运营使用交易。

答案 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中没有更多警报证明)。