【问题标题】:EF Code First Single foreign key to multiple parentsEF Code First 多个父级的单个外键
【发布时间】:2013-09-27 14:08:30
【问题描述】:

希望你能帮忙

我有 3 个(以及更多)表 [Customer] [Order][Invoice]

所有这些表都有一个 ID 列 [ID - Guid]

我有一张表 [Notes],它由 2 列组成(为简洁起见):-

[ID - Guid]
[ParentFKID - Guid]
[Comment - String]

我希望在 3 个(以及更多未来)“父”实体中的每一个上创建一个 [Notes] 集合,并配置每个实体以填充 [Notes] 表中的集合,其中主键有问题的父表指向[ParentFKID] 列。

所以基本上[ParentFKID] 列是一个外键,但它是多个其他表的外键。这是可能的,因为我们使用的是 GUID 键。

这本质上是因为我们没有复制 [CustomerNotes] [OrderNotes][InvoiceNotes] 的表,并且还避免使用外键 [Customer_ID] [Order_ID][Invoice_ID] 使笔记表永远增长的另一种选择

当然,必须有一种方法可以通过 Fluent API 映射来解决这个问题,但由于我对 EF Code First 和 Fluent API 还很陌生,所以我很难看到它。

我不在乎约束可能不可用 - 无论如何这都是可取的,而且对于结构来说似乎是不可能的。这肯定是一种常见的情况。

谁能帮助并提供一个示例,说明如何为模型配置实体以启用此场景?

【问题讨论】:

  • 您的解决方案可能很常见,但这并没有减少它的臭味和差劲。我建议您阅读此simple-talk.com/sql/database-administration/… 的第 1 项和第 4 项@ 考虑到您的笔记表的增长速度可能是单个表的 3 倍。降低所有笔记查找的性能。此外,没有强制执行唯一约束..您可能正在使用 GUID,但这并不意味着您的应用程序(或人为错误)不能在多个表中插入相同的 guid。

标签: c# entity-framework ef-code-first


【解决方案1】:

您可以使用 Fluent API 尝试此映射:

modelBuilder.Entity<Customer>()
    .HasMany(c => c.Notes)
    .WithRequired()
    .HasForeignKey(n => n.ParentFKID);

modelBuilder.Entity<Order>()
    .HasMany(o => o.Notes)
    .WithRequired()
    .HasForeignKey(n => n.ParentFKID);

modelBuilder.Entity<Invoice>()
    .HasMany(i => i.Notes)
    .WithRequired()
    .HasForeignKey(n => n.ParentFKID);

我在这里希望ParentFKID 是您的Note 类中的FK 的属性如果您不希望将 HasForeignKey 替换为:.Map(m =&gt; m.MapKey("ParentFKID"));


编辑

对共享 FK 列使用独立关联(即在 Note 类中使用不带 FK 属性的 MapKey)不起作用并在定义映射时引发异常。


请记住,如果您要创建,使用 Code-First EF 的数据库架构将强制执行所有 FK 约束。然后您必须在数据库中手动禁用它(或者也可以使用基于代码的自定义迁移,但我不知道是否以及如何)。

我还预计 ParentFKID 列不可为空(因此 WithRequired),因为我想,没有父级的注释没有意义。

我建议避免在 Note 类中使用父级(客户、订单和发票)的导航属性。如果您尝试加载包含其父级的注释 - 并且您必须包含所有三个可能的父级导航属性,因为您无法从给定注释中知道父级具有哪种类型 - EF 将创建 INNER JOIN 查询(由于所需的关系,并且因为它期望强制执行约束),这将不返回任何结果,甚至不返回父级。 (您可以使用WithOptional 而不是WithRequired 来绕过INNER JOIN - 尽管FK 不允许NULL 值。EF 将创建一个LEFT OUTER JOIN 然后当急切加载父母时。)

我真的不确定这一切是否会奏​​效,是否会产生意外的副作用。尽管您说这是一种常见情况,但 EF 没有明确支持没有强制约束的关系。

EF 会更好地支持的场景是拥有一个基础实体 EntityWithNotes,它带有 Notes 集合,CustomerOrderInvoice 派生自该集合。您将只在EntityWithNotesNote 之间定义一个关系。在数据库方面,您可以选择将 CustomerOrderInvoice 放入单个表(按层次结构表 (TPH) 映射),考虑到这些实体的业务含义有多么不同,这听起来确实很荒谬。我什至不会考虑。或者您将CustomerOrderInvoice EntityWithNotes 放入不同的表(Table-Per-Type (TPT) 映射)。但是,TPT 并不是真正以最佳性能而闻名。对于如此重要​​的实体和可能快速增长的表,我不会真正考虑这一点。

编辑

虽然与开头显示的共享 FK 属性的映射有效,但当我删除数据库中外键约束的强制执行时,我得到了其他异常。虽然我可以成功保存数据,但我得到了关于 ObjectContext 不一致状态的异常,因为显然上下文总是期望强制执行约束。

我建议不要使用此模型,并为CustomerNotesOrderNotesInvoiceNotes 使用单独的表。如果这是不可能的(现有且不可更改的数据库架构?)实体框架是否是此类架构的合适工具是值得怀疑的。

【讨论】:

  • 有趣。 As you may remember 我在处理这些多态关联方面遇到了很多困难。经过深思熟虑,我目前倾向于为每个父母创建单独的子表。我也怀疑“这一切是否可行”,我担心 EF 会抱怨共享外键,但我必须在这个周末的某个时候尝试。
  • @GertArnold:我打算让我最后一段中的EntityWithNotes 成为一个真正的实体(例如,有它自己的 DbSet),而不仅仅是其他三个实体的基础。我相信如果Notes 集合在该基本实体中并且Note 中的FK 直接引用它,而不是其他三个实体,那么它会起作用。我认为这不是多态关联的情况。我也不太确定共享的 FK。共享的导航属性肯定不会起作用,但共享的 FK...
  • @GertArnold:我的提示只有 50% 正确:共享 FK 与外键关联 (HasForeignKey) 一起使用,但它不适用于独立关联 (MapKey),这有点奇怪但是使用相同的列名三次使用MapKey 会引发关于名称不明确的异常。无论如何,真正的问题是删除数据库中导致其他异常的约束强制执行(参见上面的编辑)我无法真正摆脱。
  • 周末比预期的要忙...很高兴你有时间来测试这个。我同意你的最终结论。基类的想法对我也没有吸引力。三个单独的表的唯一问题是,无论所有者是谁,都更难获取注释,但这可以通过数据库中的 (UNION) 视图来解决。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多