3 c# entity-framework-6 entity-framework-core asp.net-core-mvc
我看过一本书,里面有这样的代码:
\n\npublic class Order \n{\n public int OrderID { get; set; }\n public ICollection<CartLine> Lines { get; set; }\n ...\n}\n\npublic class CartLine\n{\n public int CartLineID { get; set; }\n public Product Product { get; set; }\n public int Quantity { get; set; }\n}\n\n//Product class is just a normal class that has properties such as ProductID, Name etc\nRun Code Online (Sandbox Code Playgroud)\n\n在订单存储库中,有一个 SaveOrder 方法:
\n\npublic void SaveOrder(Order order)\n{\n context.AttachRange(order.Lines.Select(l => l.Product));\n if (order.OrderID == 0)\n {\n context.Orders.Add(order);\n }\n context.SaveChanges();\n}\nRun Code Online (Sandbox Code Playgroud)\n\n书上说:
\n\n当在数据库中存储 Order 对象时。当用户\xe2\x80\x99s 购物车数据从会话存储中反序列化时,JSON 包会创建 Entity Framework Core 未知的新对象,然后 Entity Framework Core 会尝试将所有对象写入数据库。对于 Product 对象,这意味着 Entity Framework Core 尝试写入已存储的对象,这会导致错误。为了避免这个问题,我通知 Entity Framework Core 这些对象存在,并且\xe2\x80\x99 不应该存储在数据库中,除非它们被修改
\n\n我很困惑,有两个问题:
\n\nQ1-为什么写入已经存储的对象会导致错误,从底层数据库的角度来看,这只是一个更新SQL语句,将所有列修改为其当前值?我知道它不做任何更改并重写会做不必要的工作一切,但它不应该在数据库级别引发任何错误?
\n\nQ2-为什么我们不做同样的事情CartLine:
context.AttachRange(order.Lines.Select(l => l.Product));\ncontext.AttachRange(order.Lines);\nRun Code Online (Sandbox Code Playgroud)\n\n防止CartLine像我们处理对象那样将对象存储在数据库中Product?
好吧,这会是一篇很长的文章:
第一个问题:
在实体框架(核心或“旧”6)中,有“更改跟踪”的概念。该类DbContext能够跟踪您对数据所做的所有更改,然后通过 SQL 语句(INSERT、UPDATE、DELETE)将其应用到数据库中。要了解为什么它会在您的情况下引发错误,您首先需要了解DbContext/ 更改跟踪的实际工作原理。让我们以你的例子为例:
public void SaveOrder(Order order)
{
context.AttachRange(order.Lines.Select(l => l.Product));
if (order.OrderID == 0)
{
context.Orders.Add(order);
}
context.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
在此方法中,您会收到一个Order包含Lines和 的实例Products。假设此方法是从某个 Web 应用程序调用的,这意味着您没有从数据库加载 Order 实体。这就是所谓的断开连接场景
它是“断开连接”的,因为您DbContext不知道它们的存在。当您这样做时,您context.AttachRange实际上是在告诉 EF:我在这里进行控制,并且我 100% 确定这些实体已经存在于数据库中。请暂时注意它们!,
让我们再次使用您的代码:想象它是一个新代码Order(因此它将输入您的 if )并且您删除了context.AttachRange部分代码。一旦代码到达,Add这些SaveChanges事情就会在 内部发生DbContext:
DetectChanges方法将被调用Order, Lines and Products当前图中的所有实体然后你继续打电话SaveChanges,就会失败,就像书上告诉你的那样。为什么?想象一下所Products选择的是:
Id: 1, "Macbook Pro"
Id: 2, "Office Chair"
Run Code Online (Sandbox Code Playgroud)
当DbContext查看实体但不了解它们时,它将它们添加到状态为 的待处理更改中Added。当您调用 时SaveChanges,它会INSERT根据这些产品在模型中的当前状态发出这些产品的语句。由于 ID1 and 2已存在于数据库中,因此操作失败,并出现主键冲突。
这就是为什么在这种情况下你必须调用Attach(或AttachRange)。这有效地告诉 EF 这些实体存在于数据库中,并且不应尝试再次插入它们。它们将被添加到上下文中,状态为Unchanged。Attach通常用于您之前未加载实体的情况dbContext。
第二个问题:
这对我来说很难访问,因为我不知道该级别的上下文/模型,但这是我的猜测:
您不需要对 执行此操作,Cartline因为对于每个订单,您可能都想插入新的Order line。就像在亚马逊买东西一样思考。您将产品放入购物车,它会生成一个Order,然后Order Lines,组成该订单的东西。
如果您随后要更新现有订单并向其中添加更多商品,那么您将遇到同样的问题。您必须CartLines先加载现有的,然后再将它们保存到数据库中,或者Attach像这里那样调用。
希望它能更清楚一点。我已经回答了一个类似的问题,其中提供了更多详细信息,因此也许阅读该问题也会有更多帮助: EF Core Modified Entity State 的行为如何?
| 归档时间: |
|
| 查看次数: |
3886 次 |
| 最近记录: |