使用Entity Framework Code First创建一个类,该类具有与自身相同类型的父级和子级。

7

我有一个名为Content的类,它应该能够具有parentId以进行继承,但我还希望它具有与此继承树无关的子内容列表。

我基本上想要一个名为ChildContentRelationship的链接表,在其中具有父Content和子Content的Id,并且Content类将具有ChildContentRelationship的列表。

这导致了许多错误。

这是我想做的事情:

public class Content
{
    public int Id { get; set; }

    public int? ParentContentId { get; set; }
    public virtual Content ParentContent { get; set; }

    public string Name { get; set; }

    public int ContentTypeId { get; set; }
    public virtual ContentType ContentType { get; set; }

    public virtual ICollection<Property> Properties { get; set; }

    public virtual ICollection<ChildContentRelationship> ChildContent { get; set; } 
}

我该如何在EF中设置这个?

你正在使用哪个版本的Entity Framework,采用什么样的方法(数据库优先,模型优先,代码优先)?为什么你想要一个链接表呢?难道你没有一个一对多的关系,即一个父级和多个子级吗? - Slauma
EF 4.1 +,Code First。这是由于ParentContentId引起的。我想让内容拥有一个父级,以继承来自父级内容类型架构的任何属性。但我也希望一段内容能够引用不在此树层次结构中的其他内容。这样我就可以呈现子内容,类似于控件。我可以将其命名为控件,并让控件拥有ContentId,我想这样行吗? - Dean Nolan
"Code First"已经在标题中了,我太蠢了,没有注意到,抱歉。 - Slauma
1个回答

19

我不确定我是否正确理解了你的模型,让我们讨论一下选项。

暂时不考虑额外的实体ChildContentRelationship,假设ChildContent集合的类型为ICollection<Content>

  • 选项1:

    我假设ParentContentChildContent反向属性。这意味着如果你有一个Id = x的Content,并且这个Content有一个Id = y的ChildContent,那么ChildContents的ParentContentId必须始终为x。这将只是一个单一的关联,ParentContentChildContent是同一关联的两端。

    这种关系的映射可以使用数据注释来创建...

    [InverseProperty("ParentContent")]
    public virtual ICollection<Content> ChildContent { get; set; }
    

    ... 或使用流畅API:

    modelBuilder.Entity<Content>()
        .HasOptional(c => c.ParentContent)
        .WithMany(c => c.ChildContent)
        .HasForeignKey(c => c.ParentContentId);
    

    我认为这不是你想要的("......与......毫无关系")。不过请考虑对导航属性进行重命名。如果有人看到 Parent...Child...,他很可能会认为它们是同一个关系的一对导航属性。

  • 选项2:

    ParentContent 不是 ChildContent 的反向属性,这意味着你实际上有两个独立的关系,并且两个关系的第二个端点在你的模型类中没有暴露出来。

    ParentContent 的映射应该是这样的:

    modelBuilder.Entity<Content>()
        .HasOptional(c => c.ParentContent)
        .WithMany()
        .HasForeignKey(c => c.ParentContentId);
    
    WithoutMany()中没有参数,这意味着第二个端点不是您的模型类中的属性,特别是它不是ChildContent

    现在问题还未解决: ChildContent属于什么类型的关系?它是一对多关系还是多对多关系?

    • 选项2a

      如果一个Content引用其他ChildContent,并且不能有第二个Content引用相同的ChildContentContent的子项是唯一的,可以这么说),则您就有了一个一对多的关系。 (这类似于订单和订单项之间的关系:订单项只能属于一个特定的订单。)

      ChildContent的映射如下:

      modelBuilder.Entity<Content>()
          .HasMany(c => c.ChildContent)
          .WithOptional(); // or WithRequired()
      

      在你的数据库中,Content表将会有一个额外的外键列,该列属于此关联但在实体类中没有相应的FK属性。

      选项2b:

      如果多个Content可以引用相同的ChildContent,那么你就有了一个多对多的关系。(这类似于用户和角色之间的关系:有许多用户属于同一角色,一个用户也可以拥有多个角色。)

      ChildContent的映射如下所示:

      modelBuilder.Entity<Content>()
          .HasMany(c => c.ChildContent)
          .WithMany()
          .Map(x =>
          {
              x.MapLeftKey("ParentId");
              x.MapRightKey("ChildId");
              x.ToTable("ChildContentRelationships");
          });
      

      这个映射会在数据库中创建一个连接表ChildContentRelationships,但是你不需要为这个表创建对应的实体。

    • 选项2c

      仅在多对多关系除了两个键(ParentIdChildId)之外还有更多属性(例如CreationDateRelationshipType等)时,您需要在模型中引入一个新的实体ChildContentRelationship

    • public class ChildContentRelationship
      {
          [Key, Column(Order = 0)]
          public int ParentId { get; set; }
          [Key, Column(Order = 1)]
          public int ChildId { get; set; }
      
          public Content Parent { get; set; }
          public Content Child { get; set; }
      
          public DateTime CreationDate { get; set; }
          public string RelationshipType { get; set; }
      }
      

      现在你的Content类将拥有一个ChildContentRelationship集合:

      public virtual ICollection<ChildContentRelationship> ChildContent
          { get; set; }
      

      你有两个一对多的关系:

      modelBuilder.Entity<ChildContentRelationship>()
          .HasRequired(ccr => ccr.Parent)
          .WithMany(c => c.ChildContent)
          .HasForeignKey(ccr => ccr.ParentId);
      
      modelBuilder.Entity<ChildContentRelationship>()
          .HasRequired(ccr => ccr.Child)
          .WithMany()
          .HasForeignKey(ccr => ccr.ChildId);
      

      我认为你想选择2a或2b中的一个,但我不确定。


2
谢谢你澄清这个问题,我想我正在寻找2b选项,也许命名需要澄清一下。我觉得它更符合实际的是一个控制器,所以可能需要一个不同的类/设计。明天我会尝试一下。 - Dean Nolan
1
这是一个答案! :) - Leniel Maccaferri

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接