在实体框架工作核心中更新数据库表数据的最佳方法是什么?
- 检索表行,进行更改并保存
- 在数据库上下文中使用关键字更新并处理项目不存在的异常
我们可以在EF6上使用哪些改进的功能?
Answers:
要使用Entity Framework Core更新实体,这是逻辑过程:
DbContext类的实例Update()方法DbContext:
开始跟踪处于Modified状态的给定实体,以便在
SaveChanges()调用该实体时在数据库中对其进行更新。
更新方法不会将更改保存在数据库中;相反,它为DbContext实例中的条目设置状态。
因此,我们可以在调用Update()方法之前将更改保存到数据库中。
我将假设一些对象定义来回答您的问题:
数据库名称为Store
表名称为产品
产品类别定义:
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();
        }
}
int?的ProductID?它成为可选的主键吗?
                    根据Microsoft文档:
先读方法需要额外的数据库读取,并且可能导致更复杂的代码来处理并发冲突
但是,您应该知道在DbContext上使用Update方法会将所有字段标记为已修改,并将所有字段包括在查询中。如果要更新字段的子集,则应使用Attach方法,然后将所需字段标记为手动修改。
context.Attach(person);
context.Entry(person).Property(p => p.Name).IsModified = true;
context.SaveChanges();
context.Entry(person).Property(p => p.Name).IsModified = true;
                    context.Entry(person).State = EntityState.Modified;
                    _context.Attach(person).State = EntityState.Modified; 指示该实体应通过SaveChanges方法进行更新。
                    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;
}
Microsoft Docs为我们提供了两种方法。
推荐的HttpPost编辑代码:读取和更新
这与我们在早期版本的Entity Framework中使用的旧方法相同。这就是Microsoft向我们推荐的。
好处
Modified在通过表单输入更改的字段上设置标志。替代的HttpPost编辑代码:创建并附加
一种替代方法是将由模型绑定器创建的实体附加到EF上下文,并将其标记为已修改。
如另一个答案中所述,先读方法需要额外的数据库读取,并且可能导致用于处理并发冲突的代码更加复杂。
经过所有的答案,我想我会添加两个简单的选择
如果您已经使用启用跟踪功能的FirstOrDefault()访问记录(不使用.AsNoTracking()函数,因为它将禁用跟踪功能)并更新了某些字段,则只需调用context.SaveChanges()
在其他情况下,您要么使用HtppPost将实体发布到服务器,要么由于某些原因禁用了跟踪,那么您应该在context.SaveChanges()之前调用context.Update(entityName)。
第一个选项将仅更新您更改的字段,而第二个选项将更新数据库中的所有字段,即使实际上未更新任何字段值:)
更通用的方法
为了简化此方法,使用“ 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"; });