Many-To-Many Relationships in EF Core Explained Simply

A many-to-many relationship is conceptually similar to two many-to-one relationships connected to each other.

While the history of of many-to-many relationships (or who created them) is a mystery, my theory is that the creators saw the limitations of one-to-many. A one-to-many can ONLY have exactly what it says: one parent and many children. But similar to real-life, a child cannot have multiple parents because that is biologically impossible (unless scientist intervene!).

In order to by pass this limitation, some smart person likely said “Let’s hook them up together via a join table and now we can have multiple relationships!”. Thus a many-to-many was born!

Understanding Many-to-Many

Many-to-many relationships are a bit more complicated because they cannot be represented in a simply way using just a foreign key. I’ve we’ve seen they need an additional table.

This entity is often called a “join table”. They contain pairs of foreign keys from other tables.

“Conventional” Many-To-Many

EF Core has unique slang to denote how we set up relationships and one way is with “Convention”. Convention basically means EF Core will detect certain patterns in the code and setup the relationship for you.

Fun Fact: EF Core can even abstract away the join table itself! (something we will get to in a minute).

A simplistic way to map this schema in EF Core consists of 3 entity types Person, Event, and PersonEvent.

public class Person
{
    public int Id { get; set; }
    public List<PersonEvent> PersonEvents { get; } = new();
}

public class Event
{
    public int Id { get; set; }
    public List<PersonEvent> PersonEvents { get; } = new();
}

public class PersonEvent
{
    public int PersonId { get; set; }
    public int EventId { get; set; }
    public Person Person { get; set; } = null!;
    public Tag Event { get; set; } = null!;
}

Skip Navigations

EF Core now allows for “skip navigations” with the introduction of two collection navigations. Essentially they provide direct access to the other side of the many-to-many.

FUN FACT (or opinion in this case): This concept is eerily similar to Java’s “bidirectional many-to-many”. Coming from Java, I believe EF Core team saw the competition had a great idea and implemented their own version (which I respect).

public class Person
{
    public int Id { get; set; }
    public List<PersonEvent> PersonEvents { get; } = new();
    public List<Event> Events { get; } = new();
}

public class Event
{
    public int Id { get; set; }
    public List<PersonEvent> PersonEvents { get; } = new();
    public List<Person> Persons { get; } = new();
}

public class PersonEvent
{
    public int PersonId { get; set; }
    public int EventId { get; set; }
    public Person Person { get; set; } = null!;
    public Event Event { get; set; } = null!;
}

You can now dot into “both sides” without have to access the PersonEvents table and EF Core will manage the relationship for you.

Just when you didn’t think it could get any better, one more thing…

New way to implement Many-To-Many EF Core

In previous versions of EF Core, to create a many-to-many relationships, it was required to have a third entity. For example, to have many-to-many between a Person and a Event, you would need a PersonEvent join table.

With Entity Framework Core 5.0, it’s no longer necessary to have this relationship and you now have the ability to only have two entities.

EF Core will join the entity transparently without a .NET class defined for it and with ZERO navigation properties.

public class Person
{
    public int Id { get; set; }
    public List<Event> Events { get; } = new();
}

public class Event
{
    public int Id { get; set; }
    public List<Person> Persons { get; } = new();
}

Behind the scenes, EF Core will create out of thin air and PersonEvent table for you.

Posted in C#