Nhibernate: другой объект с тем же значением идентификатора уже был связан с сеансом: 2, объекта:
Я получаю следующую ошибку, когда я попытался сохранить объект "Компания" в своем приложении mvc
другой объект с тем же значением идентификатора уже был связан с сеансом: 2, сущности:
Я использую контейнер IOC
private class EStoreDependencies : NinjectModule
{
public override void Load()
{
Bind<ICompanyRepository>().To<CompanyRepository>().WithConstructorArgument("session",
NHibernateHelper.OpenSession());
}
}
My CompanyRepository
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;
}
}
}
My MVC Action
[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);
}
Ответы
Ответ 1
Я знаю, что это немного поздно, и вы, возможно, уже нашли решение, но, возможно, другие могли бы выиграть от него...
Эта ошибка возникает из nHibernate, когда вы обновляете экземпляр объекта, который сохраняется в кэше. В основном nHibernate хранит ваши объекты в кеше, как только вы его загрузили, поэтому последующие вызовы получат его из кеша. Если вы обновляете экземпляр, присутствующий в кеше, nHibernate выдает эту ошибку, в противном случае это может вызвать грязные чтения и конфликты при загрузке старой копии объекта.
Чтобы обойти это, вам нужно удалить объект из кеша, используя метод Evict, например:
public ActionResult Edit(EStore.Domain.Model.Company company)
{
if (company.Id > 0)
{
**ISession.Evict(company);**
_companyRepository.Update(company);
Надеюсь, что это поможет.
Ответ 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();
}
}
Кроме того, для получения дополнительной информации см. этот ответ.
Ответ 3
Возможное решение этого - прочитать объект из базы данных, скопировать поля в объект и сохранить его. Сеанс NHibernate ничего не знает о входящем объекте, который был создан MVC Model Binder.
В некоторых случаях весь объект может не отображаться или передаваться в 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 с моделью/классом домена, но во многих случаях вы не обязательно представляете их все для обновления в представлении, поэтому вам все равно придется это делать (Can not сохранить частично пустые объекты поверх старых объектов).
Ответ 4
для более агрессивного способа вы можете использовать метод Clear()