English 中文(简体)
NHibernate:更新与合并方法
原标题:NHibernate: Update vs Merge methods

我有一个NHibernate知识库。我想对NHibernateISession中加载和卸载的对象调用Update

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

具有相同标识符值的另一个对象已与实体Eshop的会话0adc76b1-7c61-4179-bb39-a05c0152f1a1相关联。实体。通货

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

这是我的通用存储库:

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

}
最佳回答

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

问题回答

合并更新保存或更新是不同的。

通常在以下场景中使用update()或saveOrUpdate():

  • 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()执行以下操作:

  • 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

merge()是非常不同的:

  • 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

来源

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

查看您遇到的异常,很明显会话中已经存在具有相同标识符的持久实例;如果你想失去已经在会话中的实体,就必须调用Merge来处理这个特定情况

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

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

太宽泛了,无法回答,也基于意见。我将避免以这种方式概括存储库。事实上,我会如果可能的话,请避免使用NHibernate的泛型存储库。我将同时公开MergeUpdate

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

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

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

正如您在答案

发现后,我应该使用Merge

同样,正如我上面所说的,我不建议在任何地方都使用合并而不是更新

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

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





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...