我想在实体上设置外键。我在我的用户控件中公开了外部实体,并希望通过 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)捏造用户控件,以便他们可以在需要时从给定存储库加载数据(糟糕的解决方案,违反了一些基本设计原则......但可行)
任何其他想法,请?
问候,
-布伦丹