【问题标题】:Entity Framework: Update Records with Include in Net Core 3实体框架:使用包含在 Net Core 3 中的更新记录
【发布时间】:2021-03-15 23:42:56
【问题描述】:
如何使用 Entity Framework Core 中的包含更新表?以下似乎更新了客户交易,但不是产品 ID。
Customer 和 Product 的属性都发生了变化,但 CustomerId 和 ProductId 保持不变。
public void ModifyTransaction(IEnumerable<CustomerTransaction> customerTransactionList)
{
foreach (var modifyItem in customerTransactionList)
{
var existingItem = _dbContext.Set<CustomerTransaction>().Include(x => x.Product)
.FirstOrDefault(x => x.CustomerTransactionId == modifyItem.CustomerTransactionId );
if (existingItem == null)
{
_dbContext.Add(existingItem );
}
else
{
_dbContext.Entry(existingItem).State = EntityState.Modified;
}
}
_dbContext.SaveChanges();
}
使用网络核心 3.1
【问题讨论】:
标签:
c#
.net
entity-framework
.net-core
entity-framework-core
【解决方案1】:
此代码很容易出现错误,这些错误会根据场景逐渐出现。在客户端和服务器之间传递实体类时,重要的是要了解传递回服务器的对象仅仅是序列化的副本,而不是跟踪的实体。由于序列化的工作方式,其中一个 DbContext 获取两个引用具有 ID: 14 的 Product 的 Transaction 记录将引用同一个实体实例,反序列化时,同一对事务将有两个单独的对象引用,每个引用一个 Product ID:14。
根据您的示例,您至少需要执行以下操作:
foreach (var modifyItem in customerTransactionList)
{
var existingItem = _dbContext.CustomerTransactions
.Include(x => x.Product)
.SingleOrDefault(x => x.CustomerTransactionId == modifyItem.CustomerTransactionId );
var trackedProduct = _dbContext.Products.Local(x => x.ProductId == modifyItem.Product.ProductId).SingleOrDefault();
if (trackedProduct != null)
modifyItem.Product = trackedProduct;
else
_dbContext.Products.Attach(modifyItem.Product);
if (existingItem == null)
_dbContext.Add(modifyItem);
else
{
_dbContext.Entry(existingItem).CurrentValues.SetValues(modifyItem);
if(existingItem.Product.ProductId != modifyItem.Product.ProductId)
existingItem.Product = modifyItem.Product; // tracked reference.
}
}
_dbContext.SaveChanges();
}
这相当于像您一样检查现有交易。但是,我们还必须检查 DbContext 可能正在跟踪的相关实体(产品)的任何 缓存 副本。如果我们不这样做并且我们尝试添加一个具有与上下文已经跟踪的 ID 匹配的产品的交易,我们将获得 PK 违规或创建新产品 ID 的重复产品记录,具体取决于您的产品PK 已配置。 (即DatabaseGenerated.Identity 列)如果找到,我们使用本地缓存实例更新产品引用,否则我们告诉 DbContext 开始跟踪产品实例。这假定此方法不能接受 新 产品作为此调用的一部分,并且收到的产品记录应该合法存在。处理新产品和验证传入的产品需要额外的条件代码和数据库检查。从那里我们确定事务是更新还是插入。在更新的情况下,我们可以使用 CurrentValues.SetValues 跨值复制(如上)或 Automapper,或手动复制相关值。假设交易可能会更改产品,我们还会根据修改后的 ID 检查产品 ID,如果不同,我们会更新产品参考。 modifyItem.Product 此时将指向 DbContext 跟踪的引用。
使用这样的方法更新实体可能非常复杂,因为您不仅要考虑检测新记录还是现有记录,还要考虑更新对 DbContext 已经跟踪的实体的引用。我强烈建议对显式添加与更新操作采用视图模型,并尽可能以原子方式处理操作。 IE。与其传递可能包含要处理的更新或插入的事务集合,不如对每种单一类型的更改进行更精细的调用。 (操作更简单、更快,出错的地方更少)