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.
The DbContext class has a method called OnModelCreating that takes an instance of ModelBuilder as a parameter. This method is called by the framework when your context is first created to build the model and its mappings in memory.
To create Foreign Key, you need to use ForeignKey attribute with specifying the name of the property as parameter. You also need to specify the name of the table which is going to participate in relationship.
A many-to-many relationship is defined in code by the inclusion of collection properties in each of the entities - The Categories property in the Book class, and the Books property in the Category class: public class Book. { public int BookId { get; set; }
You can create such a relationship by defining a third table, called a junction table, whose primary key consists of the foreign keys from both table A and table B.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With