Here you will learn how to configure many-to-many relationships between two entities using Fluent API in Entity Framework Core.
Let's implement a many-to-many relationship between the following
Student
and Course
entities, where one student can enroll for many courses and, in the same way, one course can be joined by many students.public class Student { public int StudentId { get; set; } public string Name { get; set; } } public class Course { public int CourseId { get; set; } public string CourseName { get; set; } public string Description { get; set; } }
The many-to-many relationship in the database is represented by a joining table which includes the foreign keys of both tables. Also, these foreign keys are composite primary keys.
Convention
There are no default conventions available in Entity Framework Core which automatically configure a many-to-many relationship. You must configure it using Fluent API.
Fluent API
In the Entity Framework 6.x or prior, EF API used to create the joining table for many-to-many relationships. We need not to create a joining entity for a joining table (however, we can of course create a joining entity explicitly in EF 6).
In Entity Framework Core, this has not been implemented yet. We must create a joining entity class for a joining table. The joining entity for the above
Student
and Course
entities should include a foreign key property and a reference navigation property for each entity.
The steps for configuring many-to-many relationships would the following:
- Define a new joining entity class which includes the foreign key property and the reference navigation property for each entity.
- Define a one-to-many relationship between other two entities and the joining entity, by including a collection navigation property in entities at both sides (
Student
andCourse
, in this case). - Configure both the foreign keys in the joining entity as a composite key using Fluent API.
So, first of all, define the joining entity
StudentCourse
, as shown below.public class StudentCourse { public int StudentId { get; set; } public Student Student { get; set; } public int CourseId { get; set; } public Course Course { get; set; } }
The above joining entity
StudentCourse
includes reference navigation properties Student
and Course
and their foreign key properties StudentId
and CourseId
respectively (foreign key properties follow the convention).
Now, we also need to configure two separate one-to-many relationships between
Student
-> StudentCourse
and Course
-> StudentCourse
entities. We can do it by just following the convention for one-to-many relationships, as shown below.public class Student { public int StudentId { get; set; } public string Name { get; set; } public IList<StudentCourse> StudentCourses { get; set; } } public class Course { public int CourseId { get; set; } public string CourseName { get; set; } public string Description { get; set; } public IList<StudentCourse> StudentCourses { get; set; } }
As you can see above, the
Student
and Course
entities now include a collection navigation property of StudentCourse
type. The StudentCourse
entity already includes the foreign key property and navigation property for both, Student
and Course
. This makes it a fully defined one-to-many relationship between Student
& StudentCourse
and Course
& StudentCourse
.
Now, the foreign keys must be the composite primary key in the joining table. This can only be configured using Fluent API, as below.
public class SchoolContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("Server=.\\SQLEXPRESS;Database=EFCore-SchoolDB;Trusted_Connection=True"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<StudentCourse>().HasKey(sc => new { sc.StudentId, sc.CourseId }); } public DbSet<Student> Students { get; set; } public DbSet<Course> Courses { get; set; } public DbSet<StudentCourse> StudentCourses { get; set; } }
In the above code,
modelBuilder.Entity<StudentCourse>().HasKey(sc => new { sc.StudentId, sc.CourseId })
configures StudentId
and CourseId
as the composite key.
This is how you can configure many-to-many relationships if entities follow the conventions for one-to-many relationships with the joining entity. Suppose that the foreign key property names do not follow the convention (e.g. SID instead of StudentId and CID instead of CourseId), then you can configure it using Fluent API, as shown below.
modelBuilder.Entity<StudentCourse>().HasKey(sc => new { sc.SId, sc.CId }); modelBuilder.Entity<StudentCourse>() .HasOne<Student>(sc => sc.Student) .WithMany(s => s.StudentCourses) .HasForeignKey(sc => sc.SId); modelBuilder.Entity<StudentCourse>() .HasOne<Course>(sc => sc.Course) .WithMany(s => s.StudentCourses) .HasForeignKey(sc => sc.CId);
Note: EF team will include a feature where we don't need to create a joining entity for many-to-many relationships in future.
No comments:
Post a Comment