代码之家  ›  专栏  ›  技术社区  ›  frosty

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

  •  23
  • frosty  · 技术社区  · 16 年前

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

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

    我用的是国际奥委会集装箱

    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);
        }
    
    5 回复  |  直到 8 年前
        1
  •  37
  •   Claiton Lovato    16 年前

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

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

    public ActionResult Edit(EStore.Domain.Model.Company company) 
    { 
    
            if (company.Id > 0) 
            { 
                **ISession.Evict(company);**
                _companyRepository.Update(company);
    

    希望这有帮助。

        2
  •  10
  •   Community Mohan Dere    9 年前

    我尝试了@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
  •  4
  •   lko    13 年前

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

    在某些情况下,整个对象可能不可见或传递给视图/视图模型。保存时,应首先从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属性解析/映射到域模型/类,但在许多情况下,您不必将它们全部显示出来以便在视图中进行更新,因此您无论如何都需要这样做(不能在旧对象上保存部分空的对象)。

        4
  •  1
  •   Ziv.Ti    13 年前

    要获得更大的效果,可以使用clear()方法

        5
  •  0
  •   w00ngy    8 年前

    我刚遇到这个问题,洛瓦托的回答不起作用。不过,iko的确有成效。这里有一个更健壮的iko版本,缺点是有x2次访问数据库——一次是访问get,另一次是访问insert/update。

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

    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);
    }