如何使用实体框架核心更新记录?


79

在实体框架工作核心中更新数据库表数据的最佳方法是什么?

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

我们可以在EF6上使用哪些改进的功能?

Answers:


94

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

  1. 创建DbContext类的实例
  2. 通过键检索实体
  3. 更改实体的属性
  4. 保存更改

Update()方法DbContext

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

更新方法不会将更改保存在数据库中;相反,它为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();
        }
}

1
谢谢 。这是一个很好的用法示例。2。在db上下文中使用关键字Update并处理不存在的项目的异常。我对选择哪种方法作为最佳实践更感兴趣。
查理斯(Charith)

为什么使用可空int?ProductID?它成为可选的主键吗?
面向货币的程序员,

8
实际上,context.Products.Update行是多余的,因为一旦从上下文中检索到该实体,就会对其进行跟踪。以下是有关不同方法的出色概述:learningentityframeworkcore.com/dbcontext/modifying-data
Johan

62

根据Microsoft文档

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

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

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

30
只是很小的一部分,现在有此API的更强类型的版本: context.Entry(person).Property(p => p.Name).IsModified = true;
Guru Stron '18

2
也可以简单地做context.Entry(person).State = EntityState.Modified;
Renan Coelho

1
这个上下文是什么?Entry(person).State = EntityState.Modified; 意思?如果修改多个字段,是否应该对此行进行任何更改?
user989988 '19

1
好的,ehsan jan!; D同样,如果实体是由用户更新的,并且您不知道/关心字段是否被修改,则可以使用_context.Attach(person).State = EntityState.Modified; 指示该实体应通过SaveChanges方法进行更新。
S.Serpooshan

很棒的收获。因此,这需要上下文“围绕”,因此它知道上下文中的实体。我刚刚测试了这个。如果您尝试编辑不存在的内容,则会出现异常!:)发生一个或多个错误。(尝试更新或删除商店中不存在的实体。)
granadaCoder

29
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;
}

我认为此方法确实非常有效,但我认为该帖子的内容排在最后,因为它可能使用了更多的上下文。但是喜欢!
克劳斯先生

1
我也喜欢这个!@先生。克劳斯(Krause)会不会因为现在才几天而职位很低(到目前为止)?
Wellspring

1
我得到的是“违反主键约束'PK_Offer'。无法在对象'dbo.Offer'中插入重复的键。”。
Magnus Karlsson,

7

超级简单

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

6

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

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

这与我们在早期版本的Entity Framework中使用的旧方法相同。这就是Microsoft向我们推荐的。

好处

  • 防止发布过多
  • EFs自动更改跟踪Modified在通过表单输入更改的字段上设置标志。

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

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

如另一个答案中所述,先读方法需要额外的数据库读取,并且可能导致用于处理并发冲突的代码更加复杂。


2

经过所有的答案,我想我会添加两个简单的选择

  1. 如果您已经使用启用跟踪功能的FirstOrDefault()访问记录(不使用.AsNoTracking()函数,因为它将禁用跟踪功能)并更新了某些字段,则只需调用context.SaveChanges()

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

第一个选项将仅更新您更改的字段,而第二个选项将更新数据库中的所有字段,即使实际上未更新任何字段值:)


0

更通用的方法

为了简化此方法,使用“ 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"; });
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.