Entity Framework Code First Class with parent and children of same type as it's own class

后端 未结 1 408
挽巷
挽巷 2021-02-04 17:13

I have a class of Content which should be able to have a parentId for inheritance but also I want it to have a list of child content which is nothing to do with this inheritance

相关标签:
1条回答
  • 2021-02-04 17:32

    I am not sure if I understand your model correctly. Let's discuss the options.

    For a moment I omit this additional entity ChildContentRelationship and I assume the ChildContent collection is of type ICollection<Content>.

    • Option 1:

      I assume that ParentContent is the inverse property of ChildContent. It would mean that if you have a Content with Id = x and this Content has a ChildContent with Id = y then the ChildContents ParentContentId must always be x. This would only be a single association and ParentContent and ChildContent are the endpoints of this same association.

      The mapping for this relationship can be created either with data annotations ...

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

      ... or with Fluent API:

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

      I think this is not what you want ("...has nothing to do with..."). Consider renaming your navigation properties though. If someone reads Parent... and Child... he will very likely assume they build a pair of navigation properties for the same relationship.

    • Option 2:

      ParentContent is not the inverse property of ChildContent which would mean that you actually have two independent relationships and the second endpoint of both relationships is not exposed in your model class.

      The mapping for ParentContent would look like this:

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

      WithMany() without parameters indicates that the second endpoint is not a property in your model class, especially it is not ChildContent.

      Now, the question remains: What kind of relationship does ChildContent belong to? Is it a one-to-many or is it a many-to-many relationship?

      • Option 2a

        If a Content refers to other ChildContents and there can't be a second Content which would refer to the same ChildContents (the children of a Content are unique, so to speak) then you have a one-to-many relationship. (This is similar to a relationship between an order and order items: An order item can only belong to one specific order.)

        The mapping for ChildContent would look like this:

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

        You will have an additional foreign key column in the Content table in your database which belongs to this association but doesn't have a corresponding FK property in the entity class.

      • Option 2b

        If many Contents can refer to the same ChildContents then you have a many-to-many relationship. (This is similar to a relationship between a user and roles: There can be many users within the same role and a user can have many roles.)

        The mapping for ChildContent would look like this:

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

        This mapping will create a join table ChildContentRelationships in the database but you don't need a corresponding entity for this table.

      • Option 2c

        Only in the case that the many-to-many relationship has more properties in addition to the two keys (ParentId and ChildId) (for example something like CreationDate or RelationshipType or...) you would have to introduce a new entity ChildContentRelationship into your model:

        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; }
        }
        

        Now your Content class would have a collection of ChildContentRelationships:

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

        And you have two one-to-many relationships:

        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);
        

    I believe that you want either option 2a or 2b, but I am not sure.

    0 讨论(0)
提交回复
热议问题