(请注意,这是使用 EF 6.4.4。)
只要不需要外键属性,指定就相当简单:
modelBuilder
.Entity<Order>()
.HasOptional(o => o.Quotation)
.WithOptionalPrincipal(q => q.Order);
modelBuilder
.Entity<Quotation>()
.HasOptional(q => q.Order)
.WithOptionalDependent(o => o.Quotation);
请注意,这里同时使用了 WithOptionalPrincipal
和 WithOptionalDependent
。这样应该会在从属侧(例如示例中的引用)上给你一个单独的外键列,但没有外键属性。如果你想在另一侧使用外键,请交换“Dependent”和“Principal”。
(请注意,并不需要同时拥有上面两个定义;WithOptionalDependent
将意味着另一侧是主体,反之亦然,因此如果你愿意,可以只使用其中一个,但我发现从两侧指定关系有助于防止重复声明而导致错误;任何冲突都将导致模型错误,以让你知道你漏掉了什么。)
虽然外键列上有一个索引,但该索引没有唯一约束。尽管可能可以添加自己的唯一约束(这将需要一个 Key IS NOT NULL
过滤器),但似乎并不起作用,在某些情况下更新关系时会出现异常。我认为这与 “交换问题” 有关,其中 EF 将执行其更新操作来分别查询,因此强制唯一性将阻止 EF 通过两步 “移动” 键。
EF 似乎在内部处理关联关系,而没有唯一的 DB 约束:
- 在任一侧,分配已使用的引用会自动删除引用的其他用法。(因此,如果在打开上下文时已经存在A1 <=> B1的情况,然后您写入A1 => B2,则A1 <=> B1将被删除,而A1 <=> B2将被添加,无论您在哪一侧。)
- 如果您尝试通过多次分配相同的引用来创建重复键,则EF将抛出异常,显示“多重性约束违规”。(因此,在同一上下文中,您编写了A1 => B1和A2 => B1,或某些类似的冲突映射。)
- 如果您手动更新DB以创建重复键情况,则当EF遇到此情况时,它将抛出异常,显示“发生关系多重性约束违规...这是一个不可恢复的错误。”
在EF6中似乎不可能将属性映射到外键列(至少使用Fluent API)。尝试这样做会导致非唯一列名异常,因为它尝试为属性和关联分别使用相同的名称。
请注意,技术上来说,有两个外键(即:双向各一个)是不正确的。这样的安排实际上将是
两个 0..1 到 0..1 的关联,因为没有什么可以说明两端的键应该匹配。如果您通过 UI 和/或可能是某种数据库约束来强制执行关系,则可能会起作用。
我还注意到可能存在对 0..1 到 0..1 关联的理解误解/沟通问题。从我的理解和 EF 看待它的方式来看,这意味着它是一个可选的双向 1 对 1 关联。因此,您可以在任一侧拥有没有关系的对象。(而对于 1 对 0..1 关联,一侧的对象可以存在而没有关系,但另一侧的对象始终需要一个对象来关联。)
但是0..1到0..1并不意味着您可以让关联只在一个方向上进行而不是另一个方向。如果A1 => B1,则B1 => A1(A1 <=> B1)。您不能将B1分配给A1而不使A1与B1相关联。这就是为什么这个关联只使用单个外键的原因。我认为有些人可能试图建立一个关联,其中这种情况不成立(A1与B1相关联,但B1不与A1相关联)。但那实际上不是一个关联,而是两个0..1到0..1的关联。