4

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

但是我得到了这个异常,这意味着我应该调用Mergemethod 而不是Update.

具有相同标识符值的不同对象已与会话关联:0adc76b1-7c61-4179-bb39-a05c0152f1a1,实体: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

}
4

2 回答 2

3

发现,我应该使用 Merge ,因为 merge 将在 NHibernate 会话中考虑其状态(分离的、持久的)来决定合并或更新实体。

于 2012-05-26T08:57:09.783 回答
3

MergeUpdateSaveOrUpdate不同的。

通常 update() 或 saveOrUpdate() 用于以下场景:

  • 应用程序在第一个会话中加载一个对象
  • 对象被向上传递到 UI 层
  • 对对象进行了一些修改
  • 对象被传递回业务逻辑层
  • 应用程序通过在第二个会话中调用 update() 来保留这些修改

saveOrUpdate() 执行以下操作:

  • 如果对象已在此会话中持久存在,则不执行任何操作
  • 如果与会话关联的另一个对象具有相同的标识符,则抛出异常
  • 如果对象没有标识符属性,则保存()它
  • 如果对象的标识符具有分配给新实例化对象的值,则保存()它
  • 如果对象由 or 版本化,并且版本属性值与分配给新实例化对象的值相同,则 save() 它
  • 否则更新()对象

和 merge() 非常不同:

  • 如果存在与当前会话关联的具有相同标识符的持久实例,则将给定对象的状态复制到持久实例上
  • 如果当前没有与会话关联的持久化实例,尝试从数据库中加载它,或者创建一个新的持久化实例
  • 返回持久实例
  • 给定的实例没有与会话关联,它保持分离

来源

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

查看您得到的异常,很明显会话中已经存在具有相同标识符的持久实例;如果您愿意松开已经在会话中的实体,则Merge必须调用此特定案例。

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

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

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

另一种选择是处理如下异常(注意:不安全;我不推荐这样做):

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

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

正如您在回答中所说:

发现,我应该使用 Merge

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

因为合并将在 NHibernate 会话中考虑其状态(分离的、持久的)来决定合并或更新实体。

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

于 2019-08-14T09:57:38.610 回答