C#EF4.0 POCO - 如何使用不同的DbContext实体设置导航属性?

时间:2013-03-14 05:04:07

标签: c# entity-framework-4 domain-driven-design poco entities

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

这是catch - 外部实体最初是从另一个repository / 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()失败 - 哎呀,结果知道存储库2认为它是一个已添加的条目并尝试插入。很好,所以我们也要附上以避免这种情况:

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实体引用,动态地将它们更改为in-DbContext实体引用(如果需要,从DB加载)< / p>

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

3)在保存之前手动执行这些检查(违反DRY&amp; persistence-ignorance原则)

4)放弃数据绑定到这些属性,手动设置属性&amp;从主存储库加载实体(非常糟糕 - 意味着对数据库的额外查询以获取我们已有的数据)

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

还有其他想法,PLZ?

此致

-Brendan

1 个答案:

答案 0 :(得分:0)

鉴于存在多个DbContext个实例,您似乎有多个有界上下文在起作用。具体而言,有多个聚合在起作用,即CategoryAppointment。由于您所拥有的问题,最好只使用标识值来实现聚合之间的引用 - 没有直接对象引用。如果Appointment仅通过ID引用Category,则不会出现此问题。尽管您需要整个类别聚合以用于显示目的。可以使用read-model pattern来解决此要求。

请查看Effective Aggregate Design了解更多信息。