具有相同标识符值的不同对象已与session + nhibernate关联

时间:2012-01-02 01:27:15

标签: nhibernate

我正在使用ninject mvc 3和nhibernate,当我尝试进行更新时出现此错误,我不明白为什么。

NHibernate.NonUniqueObjectException was unhandled by user code
  Message=a different object with the same identifier value was already associated with the session: e1a7bd1f-fe1d-4c2e-a459-9fcb0106ad1d, of entity: Card
  Source=NHibernate
  EntityName=Card
  StackTrace:
       at NHibernate.Engine.StatefulPersistenceContext.CheckUniqueness(EntityKey key, Object obj)
       at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformUpdate(SaveOrUpdateEvent event, Object entity, IEntityPersister persister)
       at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsDetached(SaveOrUpdateEvent event)
       at NHibernate.Event.Default.DefaultUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
       at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
       at NHibernate.Impl.SessionImpl.FireUpdate(SaveOrUpdateEvent event)
       at NHibernate.Impl.SessionImpl.Update(Object obj)
       at CCRecomendator.Framework.Data.Repository.NhibernateRepo.Update[T](T entity) in NhibernateRepo.cs:line 33
       at CardService.EditCard(Card card, IList`1 rewardTiersToUseAfterCap) in CardService.cs:line 108
       at 
   CardController.EditCbCreditCard(CbCreditCardFrmVm vm) in CardController.cs:line 505
       at lambda_method(Closure , ControllerBase , Object[] )
       at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
       at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
       at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
       at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
       at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
  InnerException: 

我无法弄清楚为什么它仍然会在nhibernate会话中。我有一个Web请求获取卡并将其绑定到VM。

然后显示在页面上。然后我有一个被调用的save方法,我尝试将VM绑定到Card Domain,然后尝试更新它,这是我收到上述错误时。

这些是两个不同的电话,会话应该被处理掉。

  Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
            Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();

我不确定是否需要再打电话或者说什么。

我使用的是工作单元,所以我添加了一个dispose方法,但不确定何时应该调用它,如果这样可以解决我的问题

public class UnitOfWork : IUnitOfWork, IDisposable
    {
        private ITransaction transaction;
        private readonly ISession session;

        public UnitOfWork(ISession session)
        {
            this.session = session;
            session.FlushMode = FlushMode.Auto;
        }

        /// <summary>
        /// Starts a transaction with the database. Uses IsolationLevel.ReadCommitted
        /// </summary>
        public void BeginTransaction()
        {
            transaction = session.BeginTransaction(IsolationLevel.ReadCommitted);
        }

        /// <summary>
        /// starts a transaction with the database.
        /// </summary>
        /// <param name="level">IsolationLevel the transaction should run in.</param>
        public void BeginTransaction(IsolationLevel level)
        {
            transaction = session.BeginTransaction(level);
        }

        private bool IsTransactionActive()
        {
            return transaction.IsActive;
        }

        /// <summary>
        /// Commits the transaction and writes to the database.
        /// </summary>
        public void Commit()
        {
            // make sure a transaction was started before we try to commit.
            if (!IsTransactionActive())
            {
                throw new InvalidOperationException("Oops! We don't have an active transaction. Did a rollback occur before this commit was triggered: "
                                                            + transaction.WasRolledBack + " did a commit happen before this commit: " + transaction.WasCommitted);
            }

            transaction.Commit();
        }

        /// <summary>
        /// Rollback any writes to the databases.
        /// </summary>
        public void Rollback()
        {
            if (IsTransactionActive())
            {
                transaction.Rollback();
            }
        }

        public void Dispose() // don't know where to call this to see if it will solve my problem
        {
            if (session.IsOpen)
            {
                session.Close();
            }

        }


[HttpPost]
public ActionResult EditCbCard(CbCardFrmVm vm)
{
    if (ModelState.IsValid)
    {
        Card card = new Card
        {
            Id = vm.Id, // id of the record in the database
            Country = countryService.LoadCountryById(vm.SelectedCountry)
        };

        CardService.EditCard(card, rewardTiersToUseAfterCap);

    }

    ModelStateValidationWrapper wrapper = ConvertTo.ModelStateValidationWrapper(creditCardService.ValidationDictionary, ModelState);
    return Json(wrapper);
}

  public void EditCard(Card card, IList<string> rewardTiersToUseAfterCap)
    {
        try
        {
            unitOfWork.BeginTransaction();

            nhibernateRepo.Update(creditCard);

            unitOfWork.Commit();
        }
        catch (ADOException ex)
        {
            ErrorSignal.FromCurrentContext().Raise(ex);
            ValidationDictionary.AddError("DbError", ExceptionMsgs.DbError);
            unitOfWork.Rollback();
        }
        catch (SqlException ex)
        {
            ErrorSignal.FromCurrentContext().Raise(ex);
            ValidationDictionary.AddError("DbError", ExceptionMsgs.DbError);
            unitOfWork.Rollback();
        }
    }

任何人都明白为什么它会认为它在同一个会话中。

1 个答案:

答案 0 :(得分:2)

即使您处置会话,创建新卡以更新状态也是不正确的。而不是

Card card = new Card
{
    Id = vm.Id, // id of the record in the database
    Country = countryService.LoadCountryById(vm.SelectedCountry)
};

使用

Card card = unitOfWork.Get<Card>(vm.Id)
card.Country = countryService.LoadCountryById(vm.SelectedCountry);

获得正确的状态。它会在没有命中数据库的情况下为您提供缓存的实例