Entity Framework Code First class with parent and child of the same type as native class

I have a Content class that must have a parent identifier for inheritance, but I also want it to have a list of child content that has nothing to do with this inheritance tree.

I basically need a link table as a ChildContentRelationship with an identifier for parentContent and childContent in it, and the Content class will have a list of ChildContentRelationship.

This caused a lot of errors.

Here waht i kinda wanna do

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

How to set this to EF?

+7
source share
1 answer

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

At some point, I omit this optional ChildContentRelationship object, and I assume that the ChildContent collection is of type ICollection<Content> .

  • Option 1:

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

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

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

    ... or with the 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 navigation properties. If someone reads Parent... and Child... , he will most likely assume that they create a pair of navigation properties for the same relationship.

  • Option 2:

    ParentContent not the inverse of a ChildContent , which would mean that you actually have two independent relationships, and the second endpoint of both relationships does not appear in your model class.

    The mapping for ParentContent will look like this:

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

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

    Now the question remains: what is the relation of ChildContent ? Is it a one-to-many relationship, or is it a many-to-many relationship?

    • Option 2a

      If a Content refers to another ChildContent , and there cannot be a second Content that will link to the same ChildContent (children from Content unique, so to speak), then you have a one-to-many relationship. (This is similar to the relationship between order and order: an order item can only belong to one specific order.)

      The mapping for ChildContent will 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 that belongs to this association but does not have the corresponding FK property in the entity class.

    • Option 2b

      If many Content can refer to the same ChildContent , then you have a many-to-many relationship. (This is similar to the relationship between a user and roles: a single role can have many users, and a user can have many roles.)

      The mapping for ChildContent will 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 the ChildContentRelationships connection ChildContentRelationships in the database, but you do not need the corresponding object for this table.

    • Option 2c

      Only if 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 ...) will you need to introduce a new ChildContentRelationship entity in 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; } } 

      Your Content class will now have a ChildContentRelationship s collection:

       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 you need option 2a or 2b, but I'm not sure.

+18
source

All Articles