您在图表中概述的关系可能不会像您认为的那样发挥作用。
如果 TableC 上的 ParentId 旨在指向 表 A 上的 Id 或表 B 上的 Id,那将不起作用。匹配的记录必须在表 A 和表 B 中。
SQL Server 将允许这样做,并且只要在实体中声明 ParentId 并将其设置为两个关系的 FK,EF(至少 EF6)将支持这一点。阴影属性(EF6 使用Map(x => x.MapKey()))不起作用,因为它希望对两个关系使用相同的名称,至少在 EF6 中是这样。在 EF Core 中,您需要测试这是否受支持。
举以下两个例子:
我们有一个学校系统,我们有学生(表 A)和教师(表 B)。学生和教师都有一个或多个地址(表 C)
地址可能属于学生,也可能属于教师,但很少会同时属于学生和教师。因此,像您在 ParentID 之间指定一个 FK 指向 Student 和 Teacher 表的表结构没有多大意义。家长 ID 5 必须同时存在于学生和教师中,而不是其中一个,即使可以,您也不知道地址是指学生还是教师。从数据库的角度来看,您可以添加 ParentType 来指示 ParentId 是指学生还是教师,但您必须删除 ParentId 上的 FK,因为它不能指向两个表并期望只找到一个或另一个。这成为一种隐含的关系,效率不高,不能通过约束来强制执行。 (即确保 ParentId 实际上指向任一表中的一行,或正确的表等。)它也不能映射为 EF 中的关系。
可行的方法是将 Student 和 Teacher 组合成一个 Person 表,其中包含角色标识符之类的东西,以判断 Person 是 Student 还是 Teacher。这样,地址表就可以合法地拥有一个 PersonId。 (而不是 ParentId)在 EF 中,您仍然可以将 Student 和 Teacher 类都通过 Person 基类映射到 Person 表,使用 Role 作为鉴别器。在 EF 中,这称为 Table-per-Hierarcy 继承。
这适用于与公共实体(如地址)共享关系的类具有合理相关性的情况。我们可以在基类表和相关的多边表之间建立一对多的关系。如果您希望学生、教师和学校拥有一个或多个地址,则效果不佳。 (即一所学校可以有校园)一所学校不是一个“人”,也不应该被视为一个人。即使对于相关实体,这也可能是有问题的,我们想要几个特定于学生的字段而不是特定于教师的其他字段。这需要向 Person 表添加一组可为空的字段,或者从 Person 到 StudentInfo 或 TeacherInfo 表的 0-1 关系来保存学生或教师的特定详细信息。
如果我们有不相关的实体并且需要跨诸如 Student 表、Teacher 表和 School 表之类的对象之间的一对多关系,所有这些都与一个或多个 Address 记录相关,那么更好的选择是使用许多多对多关系。
学生
StudentAddress (StudentId, AddressId)
老师
TeacherAddress (TeacherId, AddressId)
学校
SchoolAddress (SchoolId, AddressId)
地址
在这种情况下,如果多对多表仅包含作为复合 PK 的 FK 而没有其他列,并且您使用的是 EF6 或 EF,那么学生、教师或学校可以拥有地址实体的集合核心 5。(我相信支持这一点)早期版本的 EF Core 不支持通过未映射的连接表自动引用,因此这需要通过映射的连接实体声明集合。 (即ICollection<StudentAddress>)
地址可以包含对学生/教师/学校集合的引用,但通常我不会费心映射这些,除非它们真的有必要。作为多对多关系,这将合法地允许同一个地址属于多个学生,而这在适当的一对多中是不允许的。
为了执行正确的一对多,地址表将被删除,地址字段将被放入每个 StudentAddress、TeacherAddress 和 SchoolAddress 表中。