问题
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 tree.
I basically wanted a link table as ChildContentRelationship with Id's for parentContent and childContent in it and the Content class would have a list of ChildContentRelationship.
This has caused a lot of errors.
Here's waht I sort of want to 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 would I set this up in EF?
回答1:
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 ofChildContent
. It would mean that if you have aContent
withId
= x and this Content has aChildContent
withId
= y then the ChildContentsParentContentId
must always be x. This would only be a single association andParentContent
andChildContent
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...
andChild...
he will very likely assume they build a pair of navigation properties for the same relationship.Option 2:
ParentContent
is not the inverse property ofChildContent
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 notChildContent
.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 otherChildContent
s and there can't be a secondContent
which would refer to the sameChildContent
s (the children of aContent
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
Content
s can refer to the sameChildContent
s 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
andChildId
) (for example something likeCreationDate
orRelationshipType
or...) you would have to introduce a new entityChildContentRelationship
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 ofChildContentRelationship
s: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.
来源:https://stackoverflow.com/questions/9241165/entity-framework-code-first-class-with-parent-and-children-of-same-type-as-its