繁体   English   中英

EF Core 中的多对一关系

[英]Many to One relationship in EF Core

我正在努力在 EF Core 中实现多对一关系。 这不是一对多,而是多对一。 我有一个名为 Order 的 object,它可以具有特定的 OrderStatus,例如 BackOrder、Rush、Fulfilled。 其中每一个都有许多与之关联的属性,我需要 Order 才能访问这些属性。 例如,每个都有不同的 DeliveryCharge,每个都有不同的 ManufacturingLine,等等。它就像一个具有附加属性的枚举。

我正在使用断开连接的应用程序,因此所有数据都以 JSON 的形式通过网络发送。

在前端查询数据和查看时,使用标准 FK 进行设置即可。 但是,当我保存时,EF 认为 OrderStatus 是一个新实体,因此它会尝试插入,从而导致键冲突。

我已经在保存期间使用“修复”过程进行处理,该过程在保存订单之前重新查询 OrderStatus 并将“新”OrderStatus object 替换为来自数据库的相同订单状态。

有没有办法在 EF 核心中声明多对一关系

这就是这种关系的样子。 在此处输入图像描述

这可能是因为您将某些 JSON 直接反序列化为数据库级实体; 序列化程序正在为您重建一个实体图,您将该图交给 EF 并说“保存这个”。 因为序列化程序创建了一个具有现有属性的新 OrderStatus,并且 EF 认为它是新的,所以它会尝试保存它。 这个问题通常可以通过为前端设置一组不同的模型来避免,通过 JSON 在浏览器和网络服务器之间穿梭,而不是用于后端,通过 EF 在 web 服务器和数据库之间穿梭。 该链中间的 web 服务器中的映射过程提供了一个机会来确保 DB 实体图是 EF 可以处理的。

这并不意味着你不能删除和保存一个数据库实体图,尽管我们确实尽量避免将数据库实体发送到前端,但是如果你保存一个被删除的数据库实体图,它有责任做一些知道实体是否为新实体的过程的一部分,如果存在,则不向 EF 发送完整的新实体。

让你的实体像这样最简单:

public class OrderStatus{
  public int Id {get;set;}
  public ICollection<Order> Orders {get;set;}

  public int DeliveryCharge....
}

public class Order{
  public int Id {get;set;}
  public int OrderStatusId {get;set;}
  public OrderStatus OrderStatus {get;set;}

  public blah LotsOf....
}

然后,如果您知道订单状态 ID 存在并想添加一个使用该现有 ID 的订单:

context.Orders.Add(new Order { OrderStatusId = 2, Other = blah });
context.SaveChanges();

或者使用发送 JSON 的“前端构建它而网络服务器只是想要它”的方法:

{
  OrderStatusId: 2,
  Other: ...
}

并不是:

{
  OrderStatus: { Id: 2, DeliveryCharge: 5, ..}
  Other: ...
}

做前者可能就像调整你的 javascript 一样简单,所以它只是在发送序列化的 object 中放置一个 ID,而不是放置整个 object; 当用户从组合中选择“$5 express delivery”时,将OrderStatusId:2放入请求实体中,而不是OrderStaus:{ Id: 2, Delivery: 5, ..}

在后一种形式中,序列化程序将为您构造另一个实体,让您面临后期处理的问题(很简单,只是有点浪费):

foreach(var o in deserializeddOrders){
  o.OrderStatusId = o.OrderStatus.Id;
  o.OrderStatus = null;
}

如果您不愿意提供 OrderStatusId 并且已经在 memory 中有一个已知的 OrderStatus(您出于某种原因下载了它),您可以:

context.Orders.Add(new Order { OrderStatus = <existing entity>, Other = blah });
context.SaveChanges();

或后处理 deser'd:

foreach(var o in deserializedOrders){
  o.OrderStatus = <existing ent>;
}

如果您没有已知实体,最简单的方法是使用您想采用的任何其他检索方法下载现有实体,并将其作为匹配实体提供:

context.Orders.Add(new Order { OrderStatus = context.OrderStatuses.Find(2), Other = blah });
context.SaveChanges();

或者作为后处理:

foreach(var o in deserializedOrders){
  o.OrderStatus = context.OrderStatus.Find(o.OrderStatus.Id); //find will only download from db once; further retrievals use a cached item
}

但是不要使用现有 ID 创建新的状态实体,因为 EF 会尝试插入它:

context.Orders.Add(new Order { OrderStatus = new () { Id = 2 }, Other = blah });
context.SaveChanges();

您还可以采取其他措施来解决此问题,例如摆弄更改跟踪器以使 EF 认为存在一个新实体,但我不是粉丝。如果您只有一个 ID,请将其弹出成对。 ..孩子的 Id 属性是预期的解决方案,因此请确保这就是发生的事情

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM