【问题标题】:nhibernate : a different object with the same identifier value was already associated with the session: 2, of entity:nhibernate:具有相同标识符值的不同对象已与会话相关联:2,实体:
【发布时间】:2010-06-06 09:47:24
【问题描述】:

当我尝试在我的 mvc 应用程序中保存我的“公司”实体时遇到以下错误

具有相同标识符值的不同对象已与会话相关联:2,实体:

我正在使用 IOC 容器

private class EStoreDependencies : NinjectModule
    {
        public override void Load()
        {

            Bind<ICompanyRepository>().To<CompanyRepository>().WithConstructorArgument("session",
                                                                                       NHibernateHelper.OpenSession());
        }
    }

我的公司存储库

public class CompanyRepository : ICompanyRepository
{
    private ISession _session;

    public CompanyRepository(ISession session)
    {
        _session = session;
    }    

    public void Update(Company company)
    {

        using (ITransaction transaction = _session.BeginTransaction())
        {

            _session.Update(company);
            transaction.Commit();
        }
    }

}

和会话助手

public class NHibernateHelper
{
    private static ISessionFactory _sessionFactory; 
    const string SessionKey = "MySession";


    private static ISessionFactory SessionFactory
    {
        get
        {
            if (_sessionFactory == null)
            {
                var configuration = new Configuration();
                configuration.Configure();
                configuration.AddAssembly(typeof(UserProfile).Assembly);
                configuration.SetProperty(NHibernate.Cfg.Environment.ConnectionStringName,
                                          System.Environment.MachineName);
                _sessionFactory = configuration.BuildSessionFactory();
            }
            return _sessionFactory;
        }
    }

    public static ISession OpenSession()
    {
        var context = HttpContext.Current;
        //.GetCurrentSession()

        if (context != null && context.Items.Contains(SessionKey))
        {
            //Return already open ISession
            return (ISession)context.Items[SessionKey];
        }
        else
        {
            //Create new ISession and store in HttpContext
            var newSession = SessionFactory.OpenSession();
            if (context != null)
                context.Items[SessionKey] = newSession;

            return newSession;
        }
    }
}

我的 MVC 操作

    [HttpPost]
    public ActionResult Edit(EStore.Domain.Model.Company company)
    {

            if (company.Id > 0)
            {

                _companyRepository.Update(company);
                _statusResponses.Add(StatusResponseHelper.Create(Constants
                    .RecordUpdated(), StatusResponseLookup.Success));
            }
            else
            {
                company.CreatedByUserId = currentUserId;
               _companyRepository.Add(company);
            }


        var viewModel = EditViewModel(company.Id, _statusResponses);
        return View("Edit", viewModel);
    }

【问题讨论】:

  • 你能在单元测试中重现这个问题吗?

标签: asp.net-mvc nhibernate


【解决方案1】:

我知道这有点晚了,您可能已经找到了解决方案,但也许其他人可以从中受益......

当您更新保存在缓存中的实体实例时,nHibernate 会引发此错误。基本上,nHibernate 将您的对象在加载后存储在缓存中,因此下一次调用将从缓存中获取它。如果更新缓存中存在的实例,nHibernate 会抛出此错误,否则可能会导致脏读和加载对象旧副本的冲突。 为了解决这个问题,您需要使用 Evict 方法从缓存中删除对象,例如:

public ActionResult Edit(EStore.Domain.Model.Company company) 
{ 

        if (company.Id > 0) 
        { 
            **ISession.Evict(company);**
            _companyRepository.Update(company);

希望这会有所帮助。

【讨论】:

  • 您好,感谢您回复此问题,实际上您的问题确实起草得很糟糕。事实证明,我的一个实体没有使用与您上面的解释相关的同一会话。
  • 这是一个黑客。我敢打赌,问题是您没有正确刷新和关闭上一个会话。
  • 问题似乎是 NHibernate 无法识别新创建的对象(在 NHibernate 会话之外)。看我的回答。
【解决方案2】:

我尝试了@claitonlovatojr 的破解,但我仍然无法处理错误。

我所要做的就是将我的ISession.Update(obj) 调用替换为ISession.Merge(obj)

在您的存储库中,更改:

public void Update(Company company)
{
    using (ITransaction transaction = _session.BeginTransaction())
    {
        //_session.Update(company);
        _session.Merge(company); // <-- this
        transaction.Commit();
    }
}

另外,有关更多信息,请参阅this answer

【讨论】:

    【解决方案3】:

    一个可能的解决方案是从数据库中读取对象,将字段复制到对象,然后保存。 NHibernate 会话对由 MVC 模型绑定器实例化的传入对象一无所知。

    在某些情况下,整个对象可能不可见或传递给 View/ViewModel。保存时应先从 NHibernate 中读取,然后更新并保存。

    Company cOrig = _companyRepository.Get(company.Id);
    cOrig.PropertyToUpdate = company.PropertyToUpdate;
    ... // Copy the properties to be updated.
    // Save the freshly retrieved object! 
    // Not the new object coming from the View which NHibernate Session knows nothing about.
    _companyRepository.Update(cOrig);
    

    这需要将 ViewModel/Class 属性解析/映射到域模型/类,但在许多情况下,您不一定要将它们全部显示在视图中以进行更新,因此无论如何您都需要这样做(不能将部分空的对象保存在旧对象之上)。

    【讨论】:

    • 这并不总是修复此异常的正确方法。问题取决于您当前 NHibernate 会话中存储的内容。在我的例子中,我正在为我正在处理的系统加载一个对象,但在某些时候我失去了线程并且也做了你所做的,并加载了同一个对象,我也试图在同一个会话中保存。 NHibernate 抛出错误的原因是:同一个对象在会话中出现了两次。我通过引用我最初加载的对象上的正确子项来解决问题,并删除了 repo 调用以加载项实例。
    【解决方案4】:

    对于更激进的方式,您可以使用 Clear() 方法

    【讨论】:

      【解决方案5】:

      我刚遇到这个,Claiton Lovato 的回答没有用。然而,伊科确实奏效了。这是 Iko 的一个更强大的版本。缺点是对 db 的 x2 次访问 - 一个用于 Get,另一个用于插入/更新。

      一个可能的解决方案是从数据库中读取对象,将字段复制到对象,然后保存。

      public void Save(Company company)
      {
      
          Company dbCompany = null;
          //update
          if (company.Id != 0)
          {
              dbCompany = _companyRepository.Get(company.Id);
              dbCompany.PropertyToUpdate = company.PropertyToUpdate;
          }
          //insert
          else
          {
              dbDefaultFreightTerm = company;
          }
          // Save either the brand new object as an insert
          // Or update the original dbCompany object with an update
          _companyRepository.SaveOrUpdate(company);
      }
      

      【讨论】:

      • 于是与 ORM 的战斗开始了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-06
      • 1970-01-01
      • 2014-04-16
      • 2013-08-30
      • 2021-09-24
      相关资源
      最近更新 更多