Many-To-Many relationship

Description Article
Table of Contents
Summary

Configuring  many-to-many relationships with Code First Model.

Description

When entity classes follow the Entity Framework conventions, it is not required to configure Many-To-Many relationships neither using DataAnnotations or Fluent API.

Database Diagram:

Operations
Configuring Many-To-Many Relationship using DataAnnotation attributes

At database relational model:

  • An intermediate table is used with a foreign key to each of the primary key of the tables

In the Code First model, Entity Framework can manage many-to-many mappings when the the database JOIN table contains only the primary keys of the related entities.
The name of the table and columns are named according the Entity Framework conventions (i.e. {CLASSNAME}_COLUMNNAME).
This mapping rule is valid also for the Database First model.

At the business object/configuration mapping model:

  • No configuration is required on the navigation properties of the related entity objects

How to do it

Domain classes definition and mapping:

public class Student
{
  public Student() { }

  public int StudentId { get; set; }

  public string StudentName { get; set; }

  public virtual ICollection<Course> Courses { get; set; }
}

public class Course
{
  public Course()
  {
    this.Students = new HashSet<Student>();
  }

  public int CourseId { get; set; }
  public string CourseName { get; set; }

  public virtual ICollection<Student> Students { get; set; }
}
Configuring Many-To-Many Relationship using Fluent API

At database relational model:

  • The “many” side table contains a foreign key column to the “one” table’s primary key

At the business object/configuration mapping model:

  • The parent entity object of a “many” relationship is configured with the HasMany() method through the correspondent navigation property
  • The child entity object of the relationship is configured with the WithMany() method through the correspondent navigation property
  • The name of the column(s) for the parent and child entity foreign keys, as well as, the name of the intermediate table are configured with the Map() method

If unidirectional, then:

  1. The navigation property to the parent class type in the child entity object can be removed
  2. The WithMany() method is replaced by the overload without parameters: The WithMany() method must explicitly be set because by default Entity Framework would apply the WithOptional() method creating a One-To-Many relationship

How to do it

Domain classes definition and mapping:

public class Student
{
  public Student() { }

  public int StudentRefId { get; set; }

  public string StudentName { get; set; }

  public virtual ICollection<Course> Courses { get; set; }
}

public class Course
{
  public Course()
  {
    this.Students = new HashSet<Student>();
  }

  public int CourseRefId { get; set; }
  public string CourseName { get; set; }

  public virtual ICollection<Student> Students { get; set; }
}

Relationship configuration on DbContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{

  modelBuilder.Entity<Student>().HasKey(std => std.StudentRefId);

  modelBuilder.Entity<Course>().HasKey(c => c.CourseRefId);

  modelBuilder.Entity<Student>()
                   .HasMany<Course>(std => std.Courses)
                   .WithMany(c => c.Students)
                   .Map(cs =>
                            {
                              cs.MapLeftKey("StudentRefId");
                              cs.MapRightKey("CourseRefId");
                              cs.ToTable("StudentCourse");
                            });

}