好吧,一对多关系称为一对多,因为关系的第一端有一个元素,另一端有许多元素。您还可以具有零或一对多关系,这仅意味着非多端的元素可以是null
(或NULL
在数据库中)。
您要定义的是一对多(或者可能是零或一对多或一对多)的关系。这样的东西在关系数据库中不存在,在实体框架中也不存在。
当您定义与 EF 的关系时,您总是需要在源类和目标类中成对的两个导航属性。可以省略其中一个导航属性,但这并不意味着您可以将此关系的末尾移动到已经属于另一个关系的另一个导航属性。
在您的特定情况下,您有两个关系,因为您的两个导航属性FirstEnd
和SecondEnd
inShaft
代表两个不同的外键。因此,您要么需要两个集合,Coupling
要么可以将现有属性Coupling.Shafts
与其中一个FirstEnd
或SecondEnd
不与两者关联。另一个参考将指Coupling
. (这就是您自己的答案中的映射会发生的情况:EF 将采用覆盖第一个映射块的第二个映射块,在 and 之间创建一个关系SecondEnd
,Shafts
然后在FirstEnd
和未公开的关系之间创建另一个关系Coupling
,而不是 Shafts
再次。)
具有两个集合的解决方案 - 在我看来更有意义 - 看起来像这样:
public class Coupling
{
public int Id { get; set; }
public virtual ICollection<Shaft> ShaftsWithFirstEndHere { get; set; }
public virtual ICollection<Shaft> ShaftsWithSecondEndHere { get; set; }
}
而这个映射:
modelBuilder.Entity<Coupling>()
.HasMany(x => x.ShaftsWithFirstEndHere)
.WithOptional(x => x.FirstEnd);
modelBuilder.Entity<Coupling>()
.HasMany(x => x.ShaftsWithSecondEndHere)
.WithOptional(x => x.SecondEnd);
您可以创建一个只读且未映射的辅助属性来将两个集合连接到一个集合,但是在两个导航集合已经加载后,这种连接将在内存中发生:
public class Coupling
{
public int Id { get; set; }
public virtual ICollection<Shaft> ShaftsWithFirstEndHere { get; set; }
public virtual ICollection<Shaft> ShaftsWithSecondEndHere { get; set; }
// not mapped to DB because it has only a getter = readonly
public IEnumerable<Shaft> Shafts
{
get { return ShaftsWithFirstEndHere.Concat(ShaftsWithSecondEndHere); }
}
}
没有一种映射可以自动进行这种连接。请注意,一对多关系中的导航集合属性只是依赖表中的外键查询的结果(=Shaft
在您的示例中)。用于填充集合的外键(Include
例如通过使用或在触发延迟加载时)由关系映射很好地定义,它只是一个键 - 要么是键,FirstEnd
要么是键,SecondEnd
但不是两者。您想要实现的是两个不同外键的两个查询的组合连接结果。而这对于关系映射是不可能的。