好吧,一对多关系称为one-to-many,因为在关系的第一端有one元素,而在关系的第一端有很多元素另一端。您还可以有 零或一对 对多的关系,这仅意味着非多端的元素可以是 null(或数据库中的 NULL)。
您要定义的是二对多(或者可能是零或一或二对多)关系。这种东西在关系型数据库中不存在,在Entity Framework中也不存在。
当您定义与 EF 的关系时,您总是需要在源类和目标类中对两个导航属性。 可以省略一个导航属性,但这并不意味着您可以将此关系的末尾移动到已经属于另一个关系的另一个导航属性。
在您的特定情况下,您有两个关系,因为Shaft 中的两个导航属性FirstEnd 和SecondEnd 代表两个不同的外键。因此,您要么需要Coupling 中的两个集合,要么可以将现有属性Coupling.Shafts 关联到FirstEnd 或SecondEnd,但不能同时关联到两者。另一个参考将引用Coupling 中的“不可见”未公开的导航集合。 (这是您自己的答案中的映射会发生的情况:EF 将采用覆盖第一个映射块的第二个映射块,在 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 但不是两者兼而有之。您要实现的是 two 由两个不同外键查询的组合连接结果。而这对于关系映射是不可能的。