【问题标题】:Entity Framework: One-to-one and one-to-many relationship between same tables [duplicate]实体框架:相同表之间的一对一和一对多关系[重复]
【发布时间】:2021-07-06 21:47:08
【问题描述】:

在尝试为具有单个一对一关系以及与同一实体的一对多关系的属性配置 Fluent API 时,我遇到了间接问题。例如:

public class Person
{
   public int Id { get; set; }
   public int PrimaryNameId { get; set; }
   public NameInfo PrimaryName { get; set; } // one-to-one
   public IList<NameInfo> Names { get; set; } // one-to-many
}
public class NameInfo
{
   public int Id { get; set; }
   public int PersonId { get; set; }
   public Person Person { get; set; }
   public string Name { get; set; }
   ...
}

我试图捕捉一个人可以有多个名字的关系,但在大多数情况下,我只对他们的“PrimaryName”感兴趣。 我希望在 IsPrimary 的 NameInfo 表上没有悬挂列。对于 >90% 的记录,该列最终将为 NULL/FALSE。

当我尝试为此手动配置 Fluent API 时

**Configuration on Person Entity**

        builder.HasOne(c => c.PrimaryName)
            .WithOne()
            .HasForeignKey<Person>(c => c.PrimaryNameId)
            .OnDelete(DeleteBehavior.Restrict)
            .IsRequired();

        builder.HasMany(c => c.Names)
            .WithOne(n => n.Person)
            .HasForeignKey(n => n.PersonId);

似乎存在相互指向的外部索引键约束,从而导致数据库播种失败。为了添加 NameInfo,我需要 PersonId - 但为了添加 Person,我需要现有 NameInfo Id(用于所需的 PrimaryNameId -> Primary Name)。

【问题讨论】:

    标签: c# .net entity-framework entity-framework-core .net-5


    【解决方案1】:

    相对而言,您创建了一个与 EF 无关的 catch-22。您希望拥有的表格:

    Person
    PersonId [PK Not NULL]
    PrimaryNameId [FK Not NULL]
    

    Name
    NameId [PK Not NULL]
    PersonId [FK Not NULL]
    

    必须付出一些,因为你不能在另一个之前插入一个。

    尝试在对象级别以这种方式反规范化以具有“Names”和“PrimaryName”引用的麻烦在于,通过在您的 Person 中有 PrimaryNameId,无法强制执行 The Name 记录指出by PrimaryName 实际上与该人相关联。出于同样的原因,IsPrimaryName 不是一个非常可靠的选项,没有办法强制两个或更多记录不会以 IsPrimaryName = True 结束。您只能做出尽可能多的假设并运行数据验证检查,以防止事后出现无效组合。

    当涉及到一对一关系时,这样做的原因应该是为了提高性能。提取到一对一的数据应该很少需要和/或检索起来很昂贵。如果客户必须有一组名称值,并且可以有其他名称,那么正确的解决方案是:

    public class Person
    {
       public int Id { get; set; }
       public string Name { get; set; }
       // ... additional name-specific fields.
       public virtual ICollection<AdditionalName> AdditionalNames { get; set; } // one-to-many
    }
    

    必需的名称详细信息并不昂贵,而且它们很可能在大多数情况下都被使用/有用。尝试将它们规范化为 NameInfo 表没有任何好处,因为 Person 可能有其他名称。与其尝试将 NameInfo 表标准化为有两个目的(一个必需的名称和可选的附加名称),我建议只为可选的附加名称创建一个 AdditionalName 表。

    【讨论】:

    • 我认为您的解决方案主要假设 NameInfo 只是一个字符串。 NameInfo 不仅仅包含一个名称。如果实体是 Person 有一个 Pet 类型,它是 FavoritePet 和他们拥有的 ICollection Pets 列表,你将如何解决这个问题?
    • 在这种情况下,并不是每个人都一定有一只最喜欢的宠物,所以一个可以为空的 FK 是合理的并删除了 Catch-22,尽管这并没有放弃数据模型中没有任何内容的假设强制 FavoritePetId 指向那个人。 (我的 FavoritePetId 可以很容易地指向您的一只宠物。)如果您有比 Name 更重要的东西需要标记为 Primary,那么我建议采用“Rank”作为 PersonId 旁边的唯一索引。首选/最喜欢的是最低或最高等级。
    • 我没有假设 Name 只包含一个字符串。我的示例清楚地说明了 `//.. 附加名称特定字段'。我的观点是,如果有 1 个字段或 20 个字段并不重要,如果字段对于 1-1 关系中的客户是 必需 的,那么在客户中声明它们绝对没有区别表或相邻表。客户需要这些名称字段,客户还可以有可选的名称字段(存储在 1-many 表中)您是提出关于不可能数据场景的问题的人,然后添加一个稻草人来证明它的合理性。跨度>
    猜你喜欢
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 2013-11-24
    • 2013-02-13
    • 1970-01-01
    • 2016-07-19
    • 1970-01-01
    相关资源
    最近更新 更多