【发布时间】:2018-03-21 08:00:54
【问题描述】:
在 Entity Framework Core 中更新数据库表数据的最佳方法是什么?
- 检索表格行,进行更改并保存
- 在数据库上下文中使用关键字更新并处理项目不存在的异常
相比 EF6,我们可以使用哪些改进的功能?
【问题讨论】:
标签: entity-framework-6 .net-core entity-framework-core
在 Entity Framework Core 中更新数据库表数据的最佳方法是什么?
相比 EF6,我们可以使用哪些改进的功能?
【问题讨论】:
标签: entity-framework-6 .net-core entity-framework-core
要使用 Entity Framework Core 更新实体,这是逻辑过程:
DbContext 类创建实例DbContext 中的Update() 方法:
开始跟踪处于 Modified 状态的给定实体,以便在调用
SaveChanges()时在数据库中更新它。
Update 方法不会在数据库中保存更改;相反,它为 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?它成为可选的主键?
读取优先的方法需要额外的数据库读取,并且可能导致处理并发冲突的代码更复杂
但是,您应该知道,在 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;
}
【讨论】:
超级简单
using (var dbContext = new DbContextBuilder().BuildDbContext())
{
dbContext.Update(entity);
await dbContext.SaveChangesAsync();
}
【讨论】:
Microsoft Docs 为我们提供了两种方法。
推荐 HttpPost 编辑代码:阅读和更新
这与我们在以前版本的 Entity Framework 中使用的旧方法相同。这就是微软为我们推荐的。
优势
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"; });
【讨论】: