0

我想在实体上设置外键。我在我的用户控件中公开了外部实体,并希望通过 WinForms 数据绑定对其进行设置。

这是一个问题 - 外部实体最初是从另一个存储库/DbContext 加载的,因为用户控件使用自己的存储库独立地填充自身。

不幸的是,这不能“开箱即用”,如本例所示:

var repository1 = GetRepository();
var categoryFromRepository1 = repository1.GetAll<Category>().First();

var repository2 = GetRepository();
var appointmentFromRepository2 = repository2.GetNewAppointment();

appointmentFromRepository2 .Category = categoryFromRepository1;

repository2.Add(appointmentFromRepository2);

repository2.SaveChanges();

这在 Add() 处失败,并出现以下错误:

An entity object cannot be referenced by multiple instances of IEntityChangeTracker.

好的,所以 repository2 无法自动附加 Category,因为它已附加到 repository1。太好了,所以让我们先分离:

repository1.Detach(categoryFromRepository1);

由于验证错误而在 SaveChanges() 上失败 - 哎呀,原来 repository2 认为它是一个已添加的条目并尝试插入。太好了,所以让我们附上以避免这种情况:

repository2.Attach(categoryFromRepository1);

这行得通!问题解决了。我现在已将 repository2-entity 属性设置为 repository1-entity,瞧。

除了这个解决方案吸水……我们在整​​个程序中有许多数据绑定的自填充用户控件,并且在 SaveChanges() 之前手动分离/重新附加所有外部实体引用是一个可怕的解决方案。此外,假设我们正在保存的存储库碰巧已经附加了对象,那么当我们 Attach() 时会出现此错误:

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

我能想到的解决方案都不是那么好:

1) 在 SaveChanges() 上的通用存储库类中,扫描所有已修改实体上的所有外部引用以查找 DbContext 外实体引用,将它们动态更改为 DbContext 内实体引用(必要时从 DB 加载)

2)根本不设置导航属性,只设置外键ID字段(sucks0rz yo' b0x0rz)

3) 在保存之前手动进行这些检查(违反 DRY 和持久性无知原则)

4)放弃对这些属性的数据绑定,手动设置属性并从主存储库加载实体(可怕 - 意味着对数据库的额外查询以获取我们已经拥有的数据)

5)捏造用户控件,以便他们可以在需要时从给定存储库加载数据(糟糕的解决方案,违反了一些基本设计原则......但可行)

任何其他想法,请?

问候,

-布伦丹

4

1 回答 1

0

鉴于存在多个DbContext实例,您似乎有多个有界上下文在起作用。具体来说,有多个聚合在起作用,即CategoryAppointment。由于您遇到的问题,最好只使用标识值来实现聚合之间的引用 - 没有直接的对象引用。如果仅通过 IDAppointment引用Category,您将不会遇到此问题。尽管您可能需要整个 Category 聚合来进行显示。这个要求可以通过使用读取模型模式来解决。

查看有效的聚合设计以了解更多信息。

于 2013-03-14T19:45:55.727 回答