【问题标题】: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。与其传递可能包含要处理的更新或插入的事务集合,不如对每种单一类型的更改进行更精细的调用。 (操作更简单、更快,出错的地方更少)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-16
    • 2021-09-21
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多