【发布时间】:2026-01-20 11:15:01
【问题描述】:
我有一个包含很多实体的项目,但其中一些以一种有趣的方式链接。 User 和 PurchaseOrder 在它们之间都有两个多对多关系,以跟踪 1) 给定用户是否查看过采购订单,或 2) 需要提醒采购订单发生变化。我使用流利的 EF 映射。
其余部分是针对我的情况的信息,但基本问题是:我应该如何在 EF 中最好地处理这种设置(两个表之间有两个独立的 M:N 关系)?强>
一个大大简化的图表:
我尝试了下面的代码,但在调用 .Clear() 时收到错误消息。这个错误可能并不准确,从那时起我已经更改了很多代码。
A relationship from the 'PurchaseOrder' AssociationSet is in the 'Deleted' /
state. Given multiplicity constraints, a corresponding 'User' must also in /
the 'Deleted' state.
如果我尝试使用po.UsersViewed.Add(currentUser) 添加到列表中,然后使用_poService.Update(po),我会收到此错误。 currentUser 和 po 都是从数据库中提取的代理。
An error occurred while saving entities that do not expose foreign key /
properties for their relationships. The EntityEntries property will return /
null because a single entity cannot be identified as the source of the exception.
User类:
public int Id { get; set; }
public Guid UserGuid { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public virtual ICollection<PurchaseOrder> PurchaseOrdersViewed { get; set; }
public virtual ICollection<PurchaseOrder> PurchaseOrdersAlert { get; set; }
PurchaseOrder类:
public int Id { get; set; }
public string PONumber { get; set; }
public virtual ICollection<User> UsersViewed { get; set; }
public virtual ICollection<User> UsersAlert { get; set; }
PurchaseOrder fluent 映射的相关部分如下。 User 映射没有相关行。
HasMany(s => s.UsersViewed)
.WithMany(s => s.PurchaseOrdersViewed)
.Map(cs =>
{
cs.MapLeftKey("UserId");
cs.MapRightKey("PurchaseOrderId");
cs.ToTable("PurchaseOrderViewed");
});
HasMany(s => s.UsersAlert)
.WithMany(s => s.PurchaseOrdersAlert)
.Map(cs =>
{
cs.MapLeftKey("UserId");
cs.MapRightKey("PurchaseOrderId");
cs.ToTable("PurchaseOrderAlert");
});
这些代码几乎总是被此代码调用。 po 是使用 EF 从数据库中提取的 PurchaseOrder 实体的代理。
po.PurchaseOrdersAlert = po.PurchaseOrdersViewed;
po.PurchaseOrdersViewed.Clear();
【问题讨论】:
-
不要在数据库中建模您的业务逻辑,您可以在一个多对多表中使用枚举器进行管理,为用户声明 PurchaseOrder 的状态。
-
我同意@JanneMatikainen。这也是我的第一个想法。但即便如此,您也不应该将一个列表分配给另一个列表。 EF 实际上会在您使用 ICollection
的任何地方创建 EntityCollection 的实例,并且这些实例具有一些逻辑来跟踪更改。好的做法是保护集合设置器。 -
@JanneMatikainen 你的意思是只保留一个带有
PurchaseOrderId和UserId的连接表,然后是一个附加列来确定它是否被查看或读取(使用int常量)?如果用户没有将其标记为已查看并且没有警报,我将完全删除该条目。 -
@Vaindil:如果你保留两个列表,你可以做一个 po.PurchaseOrdersAlert.AddRange(po.PurchaseOrdersViewed);而不是分配。
-
如果您使用@Janne Matikainen 方法更改它,您将创建一个具有 UserId 和 PurchaseOrderId 的新实体来模拟多对多。添加 State 属性(具有 Viewed 和 Alert 值的枚举)。有了这个,您的两个实体只需要一个列表,并且您将状态设置为警报,而不是将它们从一个列表移动到另一个列表。
标签: c# asp.net-mvc entity-framework linq