Repository模式中,Update和Delete总是失败及其解析(报错“another entity of the same type already has the same primar”)

在Repository模式中,我的Update方法总是无法更新实体,这个非常郁闷,Update方法如下:

   1:  public virtual void Update(T entity)
   2:          {
   3:              try
   4:              {
   5:                  if (entity == null) throw new ArgumentNullException("实体类为空");
   6:                  Context.Entry(entity).State = EntityState.Modified;
   7:                  //Context.SaveChanges();
   8:              }
   9:              catch (DbEntityValidationException dbex)
  10:              {
  11:                  var msg = string.Empty;
  12:                  foreach (var validationErrors in dbex.EntityValidationErrors)
  13:                      foreach (var validateionError in validationErrors.ValidationErrors)
  14:                          msg += string.Format("Property:{0} Error:{1}", validateionError.PropertyName, validateionError.ErrorMessage);
  15:   
  16:                  var fail = new Exception(msg, dbex);
  17:                  throw fail;
  18:              }
  19:          }

 

看上去是没有任何问题的代码,一旦有实体更新的时候,总会出现如下的错误提示:

Attaching an entity of type 'TinyFrame.Data.DomainModel.t_user_application' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

看字面意思,好像是我的EntityState设置不正确导致的,虽然我尝试过重新设置几次EntityState,但是仍旧无法解决我的问题。

然后实在找不出原因,就利用关键字 “ EF Repository Update ”在Google上面搜集,果然找到一篇文章:Advanced Entity Framework 6 Scenarios for an MVC 5 Web Application (12 of 12),其中有一段话,提出了问题的所在:

 

This happened because of the following sequence of events:
 
    The Edit method calls the ValidateOneAdministratorAssignmentPerInstructor method, which retrieves all departments that have Kim Abercrombie as their administrator. That causes the English department to be read. As a result of this read operation, the English department entity that was read from the database is now being tracked by the database context.
    The Edit method tries to set the Modified flag on the English department entity created by the MVC model binder, which implicitly causes the context to try to attach that entity. But the context can't attach the entry created by the model binder because the context is already tracking an entity for the English department.
 
One solution to this problem is to keep the context from tracking in-memory department entities retrieved by the validation query. There's no disadvantage to doing this, because you won't be updating this entity or reading it again in a way that would benefit from it being cached in memory.
 

问题的原因如下:

在Context对象中,已经hold住了一个需要操作的对象,当我们把EntityState修改成modified的时候,Context会再次去加载那个操作对象,但是这样加载是无法成功的,因为当前已经存在一个对象了,再加载会导致重复,然后抛出失败的错误。

解决方法很简单,就是在展示列表的时候,利用AsNoTracking将Hold住的对象释放掉即可。我们修改代码如下:

 
 
   1:   public virtual T Get(Expression<Func<T, bool>> where)
   2:          {
   3:              return Dbset.Where(where).AsNoTracking().FirstOrDefault<T>();
   4:          }
   5:   
   6:          public virtual IQueryable<T> GetMany(Expression<Func<T, bool>> where)
   7:          {
   8:              return Dbset.Where(where).AsNoTracking();
   9:          }

 

 

然后提交,OK,问题解决。

 

=============================Update 2014.09.19======================

看到评论中有朋友虽然按照上述方法,但是仍然无法解决这一问题。原因是在Context中还保留有当前实体的副本所致,这里只要我们将实体副本从内存中完全移除,就可以了。

复制代码
   //用于监测Context中的Entity是否存在,如果存在,将其Detach,防止出现问题。
        private Boolean RemoveHoldingEntityInContext(T entity)
        {
            var objContext = ((IObjectContextAdapter)_context).ObjectContext;
            var objSet = objContext.CreateObjectSet<T>();
            var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);

            Object foundEntity;
            var exists = objContext.TryGetObjectByKey(entityKey, out foundEntity);
          
            if (exists)
            {
                objContext.Detach(foundEntity);
            }

            return (exists);
        }
复制代码

然后在Repository中,在进行更新和删除之前,运行一下即可:

复制代码
  public T Remove(T entity)
        {
            try
            {
                RemoveHoldingEntityInContext(entity);

                _context.DbSet<T>().Attach(entity);
                return _context.DbSet<T>().Remove(entity);
            }
            catch (DbEntityValidationException dbex)
            {
                var msg = string.Empty;
                foreach (var validationErrors in dbex.EntityValidationErrors)
                    foreach (var validateionError in validationErrors.ValidationErrors)
                        msg += string.Format("属性:{0} 错误:{1}", validateionError.PropertyName, validateionError.ErrorMessage);
                var fail = new Exception(msg, dbex);
                throw fail;
            }
        }

        public T Update(T entity)
        {
            try
            {
                RemoveHoldingEntityInContext(entity);

                var updated = _context.DbSet<T>().Attach(entity);
                _context.DbContext.Entry(entity).State = EntityState.Modified;
                return updated;
            }
            catch (DbEntityValidationException dbex)
            {
                var msg = string.Empty;
                foreach (var validationErrors in dbex.EntityValidationErrors)
                    foreach (var validateionError in validationErrors.ValidationErrors)
                        msg += string.Format("属性:{0} 错误:{1}", validateionError.PropertyName, validateionError.ErrorMessage);
                var fail = new Exception(msg, dbex);
                throw fail;
            }
        }
复制代码


本文源地址:http://www.cnblogs.com/scy251147/p/3688844.html

另有一篇简短的文章:http://www.cnblogs.com/ben121011/p/4367234.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值