Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fluent API, many-to-many in Entity Framework Core

I've searched stackoverflow for a proper solution on generating a many-to-many relationship, using EF Core, Code first and Fluent API.

A simple scenario would be:

public class Person {     public Person() {         Clubs = new HashSet<Club>();     }     public int PersonId { get; set; }     public virtual ICollection<Club> Clubs { get; set; } }  public class Club {     public Club() {         Persons = new HashSet<Person>();     }     public int ClubId { get; set; }     public virtual ICollection<Person> Persons { get; set; } } 

Please correct me if im wrong but I could honestly not find a question that contains an elaborate explanation on how to do this using the described tools. Can anyone explain how this is done?

like image 217
Anonymous Avatar asked Sep 12 '17 20:09

Anonymous


People also ask

How does Entity Framework handle many-to-many relationships?

To configure many-to-many relationship Using Data Annotations, you need to create the Join Table in the model. The Join Table BookCategory will have properties for the primary key of both the table. It will have two navigational properties one each for Book and Category class.

Does EF core support fluent API?

EF Fluent API is based on a Fluent API design pattern (a.k.a Fluent Interface) where the result is formulated by method chaining. In Entity Framework Core, the ModelBuilder class acts as a Fluent API.

Does EF core have many?

The Entity Framework Core Fluent API HasMany method is used to configure the many side of a one-to-many relationship. The HasMany method must be used in conjunction with the HasOne method to fully configure a valid relationship, adhering to the Has/With pattern for relationship configuration.


1 Answers

EF Core 5.0 RC1+

As of EF Core 5.0 RC1, it's possible to do this without an explicit join table. EF Core is able to configure a mapping for the many-to-many relationship shown in your question without requiring you to create a PersonClub type.

See What's New in EF Core 5.0, RC1, Many-to-many in the official docs for more information.

Previous Versions

This is not yet possible in EF Core without using an explicit class for the join. See here for an example of how to do that.

There's an open issue on Github asking for the ability to do this without the need for an explicit class, but it has not yet been completed.

Using your scenario, the example I linked would recommend the following entity classes:

public class Person {     public int PersonId { get; set; }     public virtual ICollection<PersonClub> PersonClubs { get; set; } }  public class Club {     public int ClubId { get; set; }     public virtual ICollection<PersonClub> PersonClubs { get; set; } }  public class PersonClub {     public int PersonId { get; set; }     public Person Person { get; set; }     public int ClubId { get; set; }     public Club Club { get; set; } } 

The following OnModelCreating would then be used for setup:

protected override void OnModelCreating(ModelBuilder modelBuilder) {     modelBuilder.Entity<PersonClub>()         .HasKey(pc => new { pc.PersonId, pc.ClubId });      modelBuilder.Entity<PersonClub>()         .HasOne(pc => pc.Person)         .WithMany(p => p.PersonClubs)         .HasForeignKey(pc => pc.PersonId);      modelBuilder.Entity<PersonClub>()         .HasOne(pc => pc.Club)         .WithMany(c => c.PersonClubs)         .HasForeignKey(pc => pc.ClubId); } 

Be sure to go to the open issue I linked and voice your frustration if you feel the need.

EDIT: The open issue suggests using a simple Select to navigate through this somewhat cumbersome hierarchy. In order to get from a PersonId to a collection of Clubs, you can use SelectMany. e.g.:

var clubs = dbContext.People     .Where(p => p.PersonId == id)     .SelectMany(p => p.PersonClubs);     .Select(pc => pc.Club); 

I can't vouch for whether this is truly a "best practice", but it should certainly do the trick and I think its fair to say it's not overly ugly.

like image 124
Kirk Larkin Avatar answered Sep 20 '22 10:09

Kirk Larkin