gpt4 book ai didi

c# - NHibernate:更新与合并方法

转载 作者:行者123 更新时间:2023-11-30 17:12:21 26 4
gpt4 key购买 nike

我有一个 NHibernate 存储库。我想在 NHibernate ISession 中对加载和卸载的对象调用 Update 方法。

但是我遇到了这个异常,这意味着我应该调用 Merge 方法而不是 Update

A different object with the same identifier value was already associated with the session: 0adc76b1-7c61-4179-bb39-a05c0152f1a1, of entity: Eshop.Entities.Currency

如何概括我的存储库以避免此异常?

这是我的通用存储库:

public class NHibernateProvider : IDataProvider
{
#region Variables

private readonly object locker = new object();
private ISessionFactory sessionFactory;
private Configuration configuration;
private ITransaction transaction;

#endregion

#region Properties

private ISessionFactory SessionFactory
{
get
{
lock (locker)
{
if (Null.IsObjectNull(HttpContext.Current.Items["DataProvider"]))
{
configuration = new Configuration();

configuration.Configure();

HttpContext.Current.Items["DataProvider"] = sessionFactory = configuration.BuildSessionFactory();

HttpContext.Current.Items["DataProviderSession"] = sessionFactory.OpenSession();
}

return (HttpContext.Current.Items["DataProvider"] as ISessionFactory);
}
}
}

private ISession session;
private ISession Session
{
get
{
if (Null.IsObjectNull(HttpContext.Current.Items["DataProviderSession"]))
{
session = SessionFactory.OpenSession();

session.FlushMode = FlushMode.Auto;

HttpContext.Current.Items["DataProviderSession"] = session;
}
else
{
session = HttpContext.Current.Items["DataProviderSession"] as ISession;
}
return session;

}
}

#endregion

#region Methods


public T Get<T>(Guid ID)
{
return Session.Get<T>(ID);
}

public T Get<T>(Expression<Func<T, bool>> predicate)
{
return Session.Query<T>().Where(predicate).FirstOrDefault();
}

public IQueryable<T> GetAll<T>()
{
return Session.Query<T>();
}

public IQueryable<T> GetAll<T>(Expression<Func<T, bool>> predicate)
{
return Session.Query<T>().Where(predicate);
}

public IQueryable<T> GetAll<T>(Expression<Func<T, bool>> predicate, int currentPage, int pageSize
)
{
if (Session.Query<T>().Any(predicate))
{
return Session.Query<T>().Where(predicate).Skip<T>(currentPage*pageSize).Take(pageSize);
}
return new List<T>().AsQueryable();
}

public IQueryable<T> GetAll<T, TKey>(Expression<Func<T, bool>> predicate, int currentPage, int pageSize,
Expression<Func<T, TKey>> sortExpression)
{
if (Session.Query<T>().Any(predicate))
{
return
Session.Query<T>().Where(predicate).Skip<T>(currentPage*pageSize).Take(pageSize).OrderBy<T, TKey>(
sortExpression);
}
return new List<T>().AsQueryable();
}

public bool Exists<T>(Guid ID)
{
if (Null.IsNotObjectNull(Session.Get<T>(ID)))
{
return true;
}
return false;

}

public bool Exists<T>(Expression<Func<T, bool>> predicate)
{
return Session.Query<T>().Where(predicate).Any();
}

public void Update<T>(T targetObject, bool commit = true) where T:class
{
try
{
BeginTransaction();

Session.Update(targetObject);

CommitTransaction(commit);


}
catch (Exception)
{
RollBackTransaction();
throw;
}
}

public void Update<T>(IEnumerable<T> targetObjects, bool commit = true) where T : class
{
try
{
BeginTransaction();

foreach (var target in targetObjects)
{
Session.Update(target);
}

CommitTransaction(commit);


}
catch (Exception)
{
RollBackTransaction();
throw;
}
}

public void Insert<T>(T targetObject, bool commit = true)
{
try
{
BeginTransaction();

Session.Save(targetObject);
CommitTransaction(commit);

}
catch (Exception)
{
RollBackTransaction();
throw;
}
}



public void Insert<T>(IEnumerable<T> targetObject, bool commit = true)
{
foreach (T target in targetObject)
{
Insert<T>(target, false);
}
CommitTransaction(commit);
}


public void Delete<T>(T targetObject, bool commit = true)
{
try
{
BeginTransaction();

Session.Delete(targetObject);
CommitTransaction(commit);
}
catch (Exception)
{
RollBackTransaction();
throw;
}
}

public void Delete<T>(Guid targetID, bool commit = true)
{
try
{
BeginTransaction();

Session.Delete(Get<T>(targetID));

CommitTransaction(commit);
}
catch (Exception)
{
RollBackTransaction();
throw;
}
}


public void Delete<T>(Expression<Func<T, bool>> predicate, bool commit = true)
{
try
{
BeginTransaction();
if (Session.Query<T>().Any(predicate))
{
foreach (T element in Session.Query<T>().Where(predicate))
{
Session.Delete(element);
}
}
CommitTransaction(commit);
}
catch (Exception)
{
RollBackTransaction();
throw;
}
}

private void RollBackTransaction()
{
transaction.Rollback();
}

private void CommitTransaction(bool commit)
{
if (commit && transaction.IsActive )
{
transaction.Commit();
}
}


private void BeginTransaction()
{
if (Session.Transaction.IsActive == false)
{
transaction =Session.BeginTransaction();
}
}

#endregion

}

最佳答案

MergeUpdateSaveOrUpdate 是不同的。

Usually update() or saveOrUpdate() are used in the following scenario:

  • the application loads an object in the first session
  • the object is passed up to the UI tier
  • some modifications are made to the object
  • the object is passed back down to the business logic tier
  • the application persists these modifications by calling update() in a second session

saveOrUpdate() does the following:

  • if the object is already persistent in this session, do nothing
  • if another object associated with the session has the same identifier, throw an exception
  • if the object has no identifier property, save() it
  • if the object's identifier has the value assigned to a newly instantiated object, save() it
  • if the object is versioned by a or , and the version property value is the same value assigned to a newly instantiated object, save() it
  • otherwise update() the object

and merge() is very different:

  • if there is a persistent instance with the same identifier currently associated with the session, copy the state of the given object onto the persistent instance
  • if there is no persistent instance currently associated with the session, try to load it from the database, or create a new persistent instance
  • the persistent instance is returned
  • the given instance does not become associated with the session, it remains detached

Source

如果您想将实体的分离实例附加到当前 session ,您应该调用Merge并且相同(具有相同标识符)的持久实例可能已经存在当前 session 。如果您直接对该实体调用 UpdateSaveOrUpdate,您可能会得到 NonUniqueObjectException 异常。

查看你得到的异常,很明显 session 中已经存在具有相同标识符的持久化实例;如果您愿意松开 session 中已经存在的实体,则必须为这种特定情况 调用Merge

在上面的引用中,请注意返回的实例(通过Merge 方法)是持久实例;不是作为参数传递的那个。

How can I generalize my repository to avoid this exception?

过于宽泛,无法回答,而且还基于意见。我将避免以这种方式概括存储库。其实我会avoid generic repository with NHibernate如果可能的话。相反,我将公开MergeUpdate 方法并将其留给用户使用正确的方法;但是正如您所看到的,这最大限度地减少了通用存储库的使用。这就是为什么我宁愿避免它。

另一种方法是像下面这样处理异常(注意:不安全;我不推荐这样做):

try
{
nhSession.Update(instance);
}
catch(NonUniqueObjectException)
{
instance = nhSession.Merge(instance);
}

我不推荐这样做,因为这实际上可能会隐藏代码中的实际问题。在某些情况下,这可能会产生意外行为。您的更改可能会意外丢失,因为您在 session 中的原始实体将被覆盖。

正如您在 answer 中所说:

Found that, I should use Merge

同样,正如我上面所说,我不建议在任何地方都使用 Merge 而不是 Update

because merge will decide to merge or update the entity considering its state ( detached, persistent) in NHibernate session.

这在某些情况下可能是有益的;但在其他情况下,这可能会产生问题。我已经在上面解释过了。

关于c# - NHibernate:更新与合并方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10758439/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com