Search code examples
c#nhibernaterepository-patternunit-of-work

NHibernate: Update vs Merge methods


I have an NHibernate Repository. I want to call Update method on both loaded and unloaded objects in the NHibernate ISession.

But I get this exception which means I should call Merge method instead of 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

How can I generalize my repository to avoid this exception?

Here is my generic repository:

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

}

Solution

  • Found that, I should use Merge , because merge will decide to merge or update the entity considering its state ( detached, persistent) in NHibernate session.