【问题标题】:Entity Framework Code First Class with parent and children of same type as it's own class实体框架代码第一类,其父类和子类与其自己的类相同
【发布时间】:2012-03-03 17:05:49
【问题描述】:

我有一个 Content 类,它应该能够有一个 parentId 用于继承,但我也希望它有一个与此继承树无关的子内容列表。

我基本上想要一个作为 ChildContentRelationship 的链接表,其中包含用于 parentContent 和 childContent 的 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 中进行设置?

【问题讨论】:

  • 您使用的是哪个版本的实体框架以及哪种方法(db-first、model-first、code-first)?为什么你想要一个链接表?你不是有一个一对多的关系,一个父母和许多孩子吗?
  • EF 4.1 +,代码优先。这是因为这个 ParentContentId。请参阅我希望内容能够让父级从父级内容类型架构继承任何属性。但我也希望一段内容能够引用不在此树层次结构中的其他内容。这样我就可以渲染子内容,有点像控件。我可以将它命名为控件,并让控件具有我想的 ContentId?
  • “Code First”已经在标题中了,我傻了,我没注意到,抱歉。

标签: sql entity-framework entity-framework-4.1 ef-code-first entity-relationship


【解决方案1】:

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

暂时我省略了这个额外的实体ChildContentRelationship,我假设ChildContent 集合的类型是ICollection&lt;Content&gt;

  • 选项 1:

    我假设ParentContentChildContent逆属性。这意味着如果您有一个 ContentId = x 并且这个内容有一个 ChildContentId = y 那么 ChildContents ParentContentId 必须始终是 x。这只是一个关联,ParentContentChildContent 是同一关联的端点。

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

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

    ...或使用 Fluent 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);
    

    WithMany() 不带参数表示第二个端点不是模型类中的属性,尤其是 not ChildContent

    现在,问题仍然存在:ChildContent 属于哪种关系?是一对多还是多对多的关系?

    • 选项 2a

      如果Content 引用其他ChildContents 并且不能有第二个Content 引用相同的ChildContents(Content 的子代唯一,可以这么说)那么你就有一对多的关系。 (这类似于订单和订单商品的关系:一个订单商品只能属于一个特定的订单。)

      ChildContent 的映射如下所示:

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

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

    • 选项 2b

      如果许多Contents 可以引用同一个ChildContents,那么您就有了多对多的关系。 (这类似于用户和角色之间的关系:同一个角色中可以有多个用户,并且一个用户可以拥有多个角色。)

      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 类将拥有ChildContentRelationships 的集合:

      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,但我不确定。

【讨论】:

  • 感谢您澄清这一点,我想我正在寻找选项 2b,也许命名需求已得到澄清。我认为它更现实地是一种控制,因此可能需要不同的类/设计。我明天试试。
  • 这是一个答案! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多