Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define nested Identifying Relationships Entity Framework code first

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.

like image 943
Andy Baker Avatar asked Oct 23 '12 15:10

Andy Baker


People also ask

What is OnModelCreating in Entity Framework?

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.

How do you set a foreign key in code first approach?

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.

What is many-to-many relationships in code first approach?

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

How will you create relationship between tables in Entity Framework?

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.


1 Answers

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);
like image 116
Slauma Avatar answered Sep 21 '22 19:09

Slauma