在实体框架工作核心中更新数据库表数据的最佳方法是什么?
- 检索表行,进行更改并保存
- 在数据库上下文中使用关键字更新并处理项目不存在的异常
我们可以在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"; });