【问题标题】:How to update record using Entity Framework Core?如何使用 Entity Framework Core 更新记录?
【发布时间】:2018-03-21 08:00:54
【问题描述】:

在 Entity Framework Core 中更新数据库表数据的最佳方法是什么?

  1. 检索表格行,进行更改并保存
  2. 在数据库上下文中使用关键字更新并处理项目不存在的异常

相比 EF6,我们可以使用哪些改进的功能?

【问题讨论】:

    标签: entity-framework-6 .net-core entity-framework-core


    【解决方案1】:

    要使用 Entity Framework Core 更新实体,这是逻辑过程:

    1. DbContext 类创建实例
    2. 按键检索实体
    3. 对实体的属性进行更改
    4. 保存更改

    DbContext 中的Update() 方法:

    开始跟踪处于 Modified 状态的给定实体,以便在调用 SaveChanges() 时在数据库中更新它。

    Update 方法不会在数据库中保存更改;相反,它为 DbContext 实例中的条目设置状态。

    所以,我们可以在之前调用Update() 方法来保存数据库中的更改。

    我会假设一些对象定义来回答你的问题:

    1. 数据库名称为 Store

    2. 表名是产品

    产品类定义:

    public class Product
    {
        public int? ProductID { get; set; }
        
        public string ProductName { get; set; }
        
        public string Description { get; set; }
        
        public decimal? UnitPrice { get; set; }
    }
    

    DbContext 类定义:

    public class StoreDbContext : DbContext
    {
        public DbSet<Product> Products { get; set; }
        
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("Your Connection String");
    
            base.OnConfiguring(optionsBuilder);
        }
        
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Order>(entity =>
            {
                // Set key for entity
                entity.HasKey(p => p.ProductID);
            });
            
            base.OnModelCreating(modelBuilder);
        }
    }
    

    更新实体的逻辑:

    using (var context = new StoreDbContext())
    {
            // Retrieve entity by id
            // Answer for question #1
            var entity = context.Products.FirstOrDefault(item => item.ProductID == id);
            
            // Validate entity is not null
            if (entity != null)
            {
                // Answer for question #2
    
                // Make changes on entity
                entity.UnitPrice = 49.99m;
                entity.Description = "Collector's edition";
                
                /* If the entry is being tracked, then invoking update API is not needed. 
                  The API only needs to be invoked if the entry was not tracked. 
                  https://www.learnentityframeworkcore.com/dbcontext/modifying-data */
                // context.Products.Update(entity);
                
                // Save changes in database
                context.SaveChanges();
            }
    }
    

    【讨论】:

    • 谢谢。这是一个很好的例子,说明如何使用 2. 在 db 上下文中使用 Key word Update 并处理 item not exist 的异常。我更感兴趣的是选择哪一个作为最佳实践。
    • 你为什么使用可以为空的int? 来代替ProductID?它成为可选的主键?
    • 事实上,行 context.Products.Update 是多余的,因为一旦您从上下文中检索到实体就会被跟踪。以下是对不同方法的精彩概述:learnentityframeworkcore.com/dbcontext/modifying-data
    【解决方案2】:

    根据Microsoft docs

    读取优先的方法需要额外的数据库读取,并且可能导致处理并发冲突的代码更复杂

    但是,您应该知道,在 DbContext 上使用 Update 方法会将所有字段标记为已修改,并将所有这些字段都包含在查询中。如果要更新字段子集,应使用 Attach 方法,然后手动将所需字段标记为已修改

    context.Attach(person);
    context.Entry(person).Property(p => p.Name).IsModified = true;
    context.SaveChanges();
    

    【讨论】:

    • 只是小版本,现在有这个API的强类型版本:context.Entry(person).Property(p =&gt; p.Name).IsModified = true;
    • 也可以简单做context.Entry(person).State = EntityState.Modified;
    • 这是什么context.Entry(person).State = EntityState.Modified;意思是?如果我修改了多个字段,我应该对这一行进行任何更改吗?
    • 好点 ehsan jan! ;D 另外,如果实体是由用户更新的,而您不知道/关心哪些字段被修改,您可以使用_context.Attach(person).State = EntityState.Modified; 指示该实体应在 SaveChanges 方法中更新。
    • 很棒的收获。所以这需要上下文“坚持”,所以它知道上下文中的实体。我刚刚对此进行了测试..如果您尝试编辑不存在的内容,则会出现异常! :) 发生一个或多个错误。 (尝试更新或删除商店中不存在的实体。)
    【解决方案3】:
    public async Task<bool> Update(MyObject item)
    {
        Context.Entry(await Context.MyDbSet.FirstOrDefaultAsync(x => x.Id == item.Id)).CurrentValues.SetValues(item);
        return (await Context.SaveChangesAsync()) > 0;
    }
    

    【讨论】:

    • 我认为这种方法效果很好,但我相信这篇文章在底部,因为它可以使用更多的上下文。但喜欢它!
    • 我也喜欢! @先生。克劳斯不会因为它只有几天的历史而低(到目前为止)吗?
    • 我收到“违反 PRIMARY KEY 约束 'PK_Offer'。无法在对象 'dbo.Offer' 中插入重复键。”来自此。
    【解决方案4】:

    超级简单

    using (var dbContext = new DbContextBuilder().BuildDbContext())
    {
        dbContext.Update(entity);
        await dbContext.SaveChangesAsync();
    }
    

    【讨论】:

      【解决方案5】:

      Microsoft Docs 为我们提供了两种方法。

      推荐 HttpPost 编辑代码:阅读和更新

      这与我们在以前版本的 Entity Framework 中使用的旧方法相同。这就是微软为我们推荐的。

      优势

      • 阻止overposting
      • EFs 自动更改跟踪在表单输入更改的字段上设置Modified 标志。

      替代方式 HttpPost 编辑代码:创建并附加

      另一种方法是将模型绑定器创建的实体附加到 EF 上下文并将其标记为已修改。

      正如另一个答案中提到的,读取优先的方法需要额外的数据库读取,并且可能导致更复杂的代码来处理并发冲突。

      【讨论】:

        【解决方案6】:

        看完所有答案后,我想我会添加两个简单的选项

        1. 如果您已经使用 FirstOrDefault() 访问了记录并启用了跟踪(不使用 .AsNoTracking() 函数,因为它会禁用跟踪)并更新了一些字段,那么您可以简单地调用 context.SaveChanges()

        2. 在其他情况下,您使用 HtppPost 将实体发布到服务器,或者由于某种原因禁用了跟踪,那么您应该在 context.SaveChanges() 之前调用 context.Update(entityName)

        第一个选项只会更新您更改的字段,但第二个选项将更新数据库中的所有字段,即使实际上没有任何字段值被更新:)

        【讨论】:

          【解决方案7】:

          更通用的方法

          为了简化这种方法,使用了“id”接口

          public interface IGuidKey
          {
              Guid Id { get; set; }
          }
          

          辅助方法

          public static void Modify<T>(this DbSet<T> set, Guid id, Action<T> func)
              where T : class, IGuidKey, new()
          {
              var target = new T
              {
                  Id = id
              };
              var entry = set.Attach(target);
              func(target);
              foreach (var property in entry.Properties)
              {
                  var original = property.OriginalValue;
                  var current = property.CurrentValue;
          
                  if (ReferenceEquals(original, current))
                  {
                      continue;
                  }
          
                  if (original == null)
                  {
                      property.IsModified = true;
                      continue;
                  }
          
                  var propertyIsModified = !original.Equals(current);
                  property.IsModified = propertyIsModified;
              }
          }
          

          用法

          dbContext.Operations.Modify(id, x => { x.Title = "aaa"; });
          

          【讨论】:

          • 哪里需要实现 GuidKey 接口?使用此解决方案,静态方法在存储库中无效
          猜你喜欢
          • 2020-12-06
          • 2014-11-11
          • 1970-01-01
          • 2022-11-21
          • 2021-09-13
          • 2013-02-26
          • 1970-01-01
          相关资源
          最近更新 更多