【发布时间】:2012-12-06 21:09:10
【问题描述】:
我们有一个数据库,其中一个表包含可以是其他几个表的子记录的记录。它有一个由所有者 ID 和表名组成的“软”外键。这种(反)模式被称为“多态关联”。我们知道这不是最好的数据库设计,我们会在适当的时候改变它,但不会在不久的将来。让我展示一个简化的例子:
Event、Person 和 Product 在评论中都有记录。如您所见,没有硬性 FK 约束。
在实体框架中,可以通过将Comment 子类化为EventComment 等来支持此模型,并让Event 拥有一个EventComments 集合等:
从数据库生成基本模型后,手动添加子类和关联。 OwnerCode 是这个 TPH 模型中的判别器。请注意Event、Person 和Product 是完全不同的实体。为它们创建一个通用的基类是没有意义的。
这是数据库优先的。我们的真实模型是这样工作的,没问题。
好的。现在我们要转向代码优先。所以我开始将数据库逆向工程为代码优先模型(EF Power Tools),然后继续创建子类并映射关联和继承。试图连接到 Linqpad 中的模型。这就是麻烦开始的时候。
当尝试使用此模型执行查询时,它会抛出 InvalidOperationExeception
外键组件“OwnerId”不是“EventComment”类型的声明属性。验证它没有被明确地从模型中排除,并且它是一个有效的原始属性。
当我有双向关联并且OwnerId 被映射为Comment 中的一个属性时,就会发生这种情况。我的EventMap 类 (EntityTypeConfiguration<Event>) 中的映射如下所示:
this.HasMany(x => x.Comments).WithRequired(c => c.Event)
.HasForeignKey(c => c.OwnerId);
所以我尝试在模型中映射没有OwnerId 的关联:
this.HasMany(x => x.Comments).WithRequired().Map(m => m.MapKey("OwnerId"));
这会引发MetaDataException
指定的架构无效。错误: (10,6) : 错误 0019: 类型中的每个属性名称必须是唯一的。已定义属性名称“OwnerId”。 (11,6) : 错误 0019: 类型中的每个属性名称必须是唯一的。已定义属性名称“OwnerId”。
如果我删除三个实体评论关联中的两个,那没关系,但这当然不是治愈方法。
更多细节:
- 可以通过添加一个 DbContext 生成器项从 edmx 创建一个有效的 DbContext 模型(“第二个代码”)。 (这暂时是一种解决方法)。
- 当我将工作代码优先模型(具有一个关联)导出到 edmx (
EdmxWriter) 时,关联似乎在存储模型中,而在原始 edmx 中它们是概念模型的一部分。
那么,我怎样才能先代码创建这个模型呢?我认为关键是如何指示代码优先映射概念模型中的关联,而不是存储模型。
【问题讨论】:
-
你有没有让模型与 Code-First 一起工作?有趣的是它适用于 Db-First/EDMX。我有一个关于类似模型的问题,CodePlex 的最后一句话基本上是“不支持”和“基本的 EF 限制”(stackoverflow.com/a/14880084/270591)。但是,如果您的模型与 EDMX 一起使用,它实际上似乎是 Code-First 限制,而不是一般的 EF 限制。
-
是的,数据库优先模型有效(感谢上帝)。真实模型甚至更复杂,因为它具有复合继承鉴别器。没问题。代码优先是问题所在。你试过你的模型 db-first 吗?
-
不,从未尝试过。我们知道 Code-First 不支持所有 edmx 功能,但我真的没想到这种情况会有所不同。顺便说一句,我刚刚要求确认 CodePlex 上的那个旧问题(entityframework.codeplex.com/workitem/865,页面上的最后评论)。不确定我是否可以在已关闭的项目中得到答案。
-
也许他们会,会很有趣。我认为关键在于存储模型和概念模型之间的区别,但我无法理解为什么这会如此重要。无论如何,这一切都促使我在清理我们的模型(将其转变为更体面的 3NF 模型)方面付出更多的努力,因为这显然是一个非常基本的限制,可能会导致我们的 db-first 模型在未来的 EF 版本中也崩溃。
-
我认为问题在于 OwnerType 应该只在子类中定义(应该从 Comment 类中删除)。我可以在这里工作stackoverflow.com/questions/38275335/…
标签: c# ef-code-first foreign-keys entity-framework-5 ef-database-first