我想在实体上设置外键。我在我的用户控件中公开了外部实体,并希望通过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
答案 0 :(得分:0)
鉴于存在多个DbContext
个实例,您似乎有多个有界上下文在起作用。具体而言,有多个聚合在起作用,即Category
和Appointment
。由于您所拥有的问题,最好只使用标识值来实现聚合之间的引用 - 没有直接对象引用。如果Appointment
仅通过ID引用Category
,则不会出现此问题。尽管您需要整个类别聚合以用于显示目的。可以使用read-model pattern来解决此要求。
请查看Effective Aggregate Design了解更多信息。