【问题标题】:Entity not updating using Code-First approach实体未使用 Code-First 方法进行更新
【发布时间】:2019-10-11 15:28:44
【问题描述】:

我有这个类用于数据库操作:

public class EntityService<TEntity> : IRepository<TEntity> where TEntity : BaseModel
{

     ApplicationDbContext _context;
     private DbSet<TEntity> _entities;

     public EntityService()
     {
         _context = new ApplicationDbContext();
     }

     public virtual void Update(TEntity entity)
     {
          if (entity == null)
               throw new ArgumentNullException(nameof(entity));

          try
          {
                var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();

                dbEnt = entity;
                dbEnt.UpdatedBy = GetCurrentUser();
                dbEnt.DateUpdated = DateTime.Now;
                _context.SaveChanges();
           }
           catch (DbUpdateException exception)
           {
                throw new Exception(GetFullErrorTextAndRollbackEntityChanges(exception), exception);
           }

           //-----other methods for insert and get working fine----
}

这个类中还有其他用于insertget 的方法工作正常。只有这个更新方法不会更新实体并且不会抛出异常。

更新

我遇到了类似的问题,但在这里的功能相反:Add() method adding duplicate rows for linked models in Code-First Entity Framework

我认为这两者有相同的更改跟踪原因。但是一个是添加另一个不是更新。

【问题讨论】:

  • 如果你想更新entity为什么你又得到它并将entity放入dbEnt
  • @hassan.ef 当我这样做时它不起作用。所以我想如果我再次从数据库中获取数据库实体,那么 _context 将有它的数据库引用。因为entity只是方法参数。
  • 我认为首先你应该像这样用entity的参数更新dbEnt的参数:例如:edbEnt.Name = entity.Name并且不要使用dbEnt = entity
  • @hassan.ef 这是更新所有实体的常用方法。我不能在这里明确地分配单个属性。
  • 保存前,您可以尝试拨打context.Entry(entity).State=EntityState.Modified;

标签: c# .net asp.net-mvc entity-framework ef-code-first


【解决方案1】:

线...

var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();

...将实体对象附加到上下文并返回对该实体的引用。

然后行...

dbEnt = entity;

...将此引用替换为对进入方法的entity 变量的引用。那是不是被跟踪的实体对象。您基本上丢失了对被跟踪实体的引用,并且无法再更改它。

您应该将entity 附加到上下文并将其标记为已修改,或者像您已经做的那样获取dbEnt 并修改并保存那个 对象。两种方法各有利弊,见here

【讨论】:

    【解决方案2】:

    从 _context 获取实体后,从新的详细信息更新所有字段并将实体状态设置为已修改

    var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();
    dbEnt.Name = entity.Name;
    ...
    ...
    ...
    dbEnt.UpdatedBy = GetCurrentUser();
    dbEnt.DateUpdated = DateTime.Now;
    _context.Entry(dbEnt).State = EntityState.Modified;
    _context.SaveChanges();
    

    【讨论】:

    • 设置State = EntityState.Modified不是必须的,我什至不推荐。
    【解决方案3】:

    如果您通过 Id 找到了您的实体

    var dbEnt = _context.Set<TEntity>().Where(c => c.Id == entity.Id).First();
    

    那为什么是这条线?

    dbEnt = entity;
    

    删除上面的行,因为它将删除对被跟踪对象的引用。

    【讨论】:

      【解决方案4】:

      谢谢大家。我从你的回答中得到了很多提示。 作为@GertArnold 和 @Colonel Software 的回答暗示我像这样修改了我的代码并且它起作用了:

      //Assigning BaseModel properties
      entity.CreatedBy = dbEnt.CreatedBy;
      entity.UpdatedBy = GetCurrentUser();
      entity.DateUpdated = DateTime.Now;
      entity.DateCreated = dbEnt.DateCreated;
      
      //Changing entity states
      _context.Entry(dbEnt).State = EntityState.Detached;
      _context.Entry(entity).State = EntityState.Modified;
      
      _context.SaveChanges();
      

      【讨论】:

      • 为什么不直接修改并保存dbEnt,就像您提到的答案中发生的那样?
      • @GertArnold 试过了,但它显示重复的主键值异常。我稍后会尝试解决这个问题,但现在它正在工作,我需要尽快交付项目,所以我必须采用这个解决方案。感谢您的帮助,我从您的回答和 +1 中学到了很多东西。
      • 反对什么?这解决了我的问题。也许这不是最好的解决方案,但它是一个解决方案。
      • 那么你做的比你表现的要多。正如在另一个答案(和我的)中所说,您只需要删除dbEnt = entity;
      • 投反对票是因为您的代码几乎无法正常工作。在这里使用AddOrUpdate 很奇怪。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-09
      • 1970-01-01
      相关资源
      最近更新 更多