c# - 实体框架:使用包含在 Net Core 3 中的更新记录
问题描述
如何使用 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
解决方案
此代码很容易出现错误,这些错误会根据场景逐渐出现。在客户端和服务器之间传递实体类时,重要的是要了解传递回服务器的对象仅仅是序列化的副本,而不是跟踪的实体。由于序列化的工作方式,其中一个 DbContext 获取两个引用具有 ID: 14 的 Product 的事务记录将引用相同的实体实例,反序列化时,同一对事务将有两个单独的对象引用,每个引用一个 Product编号: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 已经跟踪的实体的引用。我强烈建议对显式添加与更新操作采用视图模型,并尽可能以原子方式处理操作。即,与其传递可能包含要处理的更新或插入的事务集合,不如对每种单一类型的更改进行更精细的调用。(操作更简单、更快,出错的地方更少)
推荐阅读
- objective-c - SwiftUI 小部件:在导入的 Objective-C 头文件中的 UILabel 参数上出现“预期类型”错误
- node.js - EJS 和条件语句
- libreoffice - 在行中查找最后填充的条目
- c - 回调函数与 C 中的 void 表达式有问题?
- angular - 当我单击子组件的输入时,会显示一个 div,我怎样才能一次制作一个或隐藏其他?
- c# - C# .NetCore HttpClient PostAsync Multipart Form-Data,带有文件附件和进度,供 PowerShell 使用
- apache-spark - 在 kubernetes 上运行 Spark 时出现错误“java.lang.UnsatisfiedLinkError:__strncpy_chk”
- kubernetes - Docker Desktop for Windows kubelet 日志位于何处?
- mysql - 在 SQL 中以分钟为步长在时间间隔之间生成时间序列
- python - 计算给定文本文件中每个单词的出现次数