matomo

Entity Framework Core Many-Many Relations
Skip to main content

Development blog

This blog contains posts mostly regarding development in asp.net core. System Engineering and DevOps topics are also covered in this blog.
Most of the content published on this homepage is a practical approach to official documentations like docs.microsoft.com. I try to link the related parts of said documentations in every post.

If you feel that you are able to improve this blog by providing any kind of update or feedback, please do so via the comments section or the contact form.

Enjoy reading!

Entity Framework Core Many-Many Relations

When working with EF Core, you will sooner or later come to a point, where you will require many-to-many relationships.

EF Core does not support this ATM out-of-the-box. You will need to create a model that represents the relation. In this case we have three models.

For example, a person has watched many movies and the movies have been watched by many persons.

Person.cs

using System;
using System.Collections.Generic;
namespace test.Models
{
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public List<Movie> WatchedMovies { get; set; }
}
}

 Movie.cs

using System;
using System.Collections.Generic;
namespace test.Models
{
public class Movie
{
public int ID { get; set; }
public string Name { get; set; }
public List<Person> WatchedBy { get; set; }
}
}

Viewing.cs

using System;
namespace test.Models
{
public class Viewing
{
public int ID { bet; set; }
public Person Person { get; set; }
public Movie Movie { get; set; }
}
}

EF will automatically ignore the List<T> when saving "Person" or "Movie" to the database and it will automatically save Person.ID and Movie.ID in the Viewing Table.

The DB Tables will look like this:

Person.cs

[ID]   [int]           IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,

Movie.cs

[ID]   [int]           IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,

Viewing.cs

[ID]       [int]           IDENTITY(1,1) NOT NULL,
[PersonID] [int] NULL,
[BetID] [int] NULL,

When you use your DBContext to load the data from the DB, you need to Include() the related items like this

using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using TestProject.Models;
namespace test.Controllers
{
public class HomeController : Controller
{
private readonly TestContext _context;

public HomeController(TestContext context)
{
_context = context;
}

public async Task<IActionResult> PersonswithoutMovies()
{
return Ok(_context.Persons.ToList());
}

public async Task<IActionResult> PersonsWithMovies()
{
return Ok(_context.Persons.Include(person => person.WatchedMovies).ToList());
}

public async Task<IActionResult> MoviesWithoutViewings()
{
return Ok(_context.Movies.ToList());
}

public async Task<IActionResult> MoviesWithViewings()
{
return Ok(_context.Movies.Include(movie => movie.WatchedBy).ToList();
}
}
}

If you need to serialize the models to JSON (which is the case if you create an API), you need to prevent an infinite loop. Otherwise the API tries to create a JSON like this

{
'ID': 1,
'Name': 'Fritz',
'WatchedMovies': [
{
'ID': 1,
'Name': 'Titanic',
'WatchedBy: [
{
'ID': 1,
'Name': 'Fritz',
'WatchedMovies': [
{
'ID': 1,
'Name': 'Titanic',
'WatchedBy': [
...
]
}
]
}
]
}
]
}

 You can do this by adding a statment to the services.AddMvc() function in StartUp.cs

using System;
// using ...
namespace test
{
// ...
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddMvc(options =>
{
// ...
}).AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

//..
}
// ...
}

 



DevOps Engineer at TIMEWARP IT Consulting GmbH

Read more posts by Marius Steinbach