How to define nested Identifying Relationships Ent

2019-02-10 09:20发布

问题:

I'm using EF5 code first in a simple test application at the moment to test various functions. I have defined an 'identifying relationship' between two entities which represent a one-to-many link. Here I define a PhotoCollection that has many child Photo entities;

public class PhotoCollection
{
    public int Id { get; set; }
    public virtual ISet<Photo> Photos { get; private set; }

    public PhotoCollection()
    {
        Photos = new HashSet<Photo>();
    }
}

public class Photo
{
    [Key, ForeignKey("Parent"), Column(Order = 1)]
    public int PhotoCollectionId { get; set; }

    [Key, Column(Order = 2)]
    public int PhotoId { get; set; }

    public virtual PhotoCollection Parent { get; set; }


    [Required, MaxLength(200)]
    public string FilePath { get; set; }

    public Photo()
    {
    }

}

and my implementation of OnModelCreating includes;

modelBuilder.Entity<Photo>().Property(p => p.PhotoId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

The result is that when I delete a PhotoCollection, all the photo entities are deleted as well which is provided by the 'identifying relationship'. Great.

My question is how do I define a further level in this object graph, let's say I want to a PhotoProperties as a one-to-many collection from Photo. In this case I'd like to delete PhotoCollection and the all appropriate Photo and PhotoProperty records will be deleted too. Using the approach above, wouldn't it be necessary to add a GrandParent property to PhotoProperty that pointed to the PhotoCollection?

Can I achieve the same result using the fluent Api in model builder?

The only examples I've managed to find online are for a single level parent>child hierarchies.

Thanks in advance.

回答1:

In my opinion this should work:

public class Photo
{
    [Key, ForeignKey("Parent"), Column(Order = 1)]
    public int PhotoCollectionId { get; set; }

    [Key, Column(Order = 2)]
    public int PhotoId { get; set; }

    public virtual PhotoCollection Parent { get; set; }

    public virtual ISet<PhotoProperty> PhotoProperties { get; private set; }

    //...
}

public class PhotoProperty
{
    [Key, ForeignKey("Parent"), Column(Order = 1)]
    public int PhotoCollectionId { get; set; }

    [Key, ForeignKey("Parent"), Column(Order = 2)]
    public int PhotoId { get; set; }

    [Key, Column(Order = 3)]
    public int PhotoPropertyId { get; set; }

    public virtual Photo Parent { get; set; }

    //...
}

Note that PhotoCollectionId in PhotoProperty doesn't refer to a PhotoCollection but is part of the composite foreign key (PhotoCollectionId,PhotoId) that refers to Photo.

And yes, you can define the whole mapping with Fluent API:

modelBuilder.Entity<PhotoCollection>()
    .HasMany(pc => pc.Photos)
    .WithRequired(p => p.Parent)
    .HasForeignKey(p => p.PhotoCollectionId);

modelBuilder.Entity<Photo>()
    .HasKey(p => new { p.PhotoCollectionId, p.PhotoId });

modelBuilder.Entity<Photo>()
    .Property(p => p.PhotoId)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

modelBuilder.Entity<Photo>()
    .HasMany(p => p.PhotoProperties)
    .WithRequired(pp => pp.Parent)
    .HasForeignKey(pp => new { pp.PhotoCollectionId, pp.PhotoId });

modelBuilder.Entity<PhotoProperty>()
    .HasKey(pp => new { pp.PhotoCollectionId, pp.PhotoId, pp.PhotoPropertyId });

modelBuilder.Entity<PhotoProperty>()
    .Property(pp => pp.PhotoPropertyId)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);