Entity Framework Core Relationships with Fluent API
Entity Framework Core (EF Core) is an Object-Relational Mapping (ORM). It works between the application and the database. To explain how we can implement the relationships of our entities, I will be using the Code First approach (with a new database), which means that first I’m going to the create the entities classes, and the EF Core will create the database and the tables, based on these entities (EF Core maps the entities to the database).
We are going to see how we can create relationships between classes using EF Core and Fluent API. For demonstration purpose, I’ve created a Console Application using .NET Core 3.1.
What is a Relationship?
A relationship defines how two entities relate to each other. In a relational database, this is represented by a foreign key constraint.
There are three kinds of relationship:
- One to Many (1:N)
- One to One (1:1)
- Many to Many (N:N)
We are going to see how we can implement each one of them.
Relationships between entities in an Entity Framework model are defined by Navigation Properties. A navigation property is one that the database provider being used cannot map to a primitive (or scalar) type.
Fluent API
EF Core follows conventions to generate the database and the tables. These conventions are the rules to create the model, based in the entities classes. With Fluent API we can override these configurations and explicitly defined the configuration that we want to set in the tables in the database.
We need to install some packages in the application. You can do it through NuGet or through the Package Manager Console with the commands:
EF Core
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Tools
SQL Server package
Install-Package Microsoft.EntityFrameworkCore.SqlServer
DbContext class
We need to create the DbContext class, which in this case will be EFCoreRelationshipsExamplesDbContext class. This class must inherit from DbContext and override the method OnConfiguring, where we must add the configuration of the database, which in this case is the localdb:
Because the focus of this article is to explain about the relationship between the entities, I’m using a very simple structure, with just one layer and simples model classes, but in a real-world project, you should create the DbContext class in a separated layer. If you want to know more about how can you create the structure of your project, you can check the article “Creating an Application from Scratch using .NET Core and Angular — Part 1” clicking here.
One-to-Many (1:N)
You will see two ways to create a One-To-Many relationship. For this first example, we will have the following scenario: we have two entities: Course and Student. For this example, a Course can have many students, and a student can only have one course, so it’s a one-to-many (1:N) relationship. For One-to-Many relationships, we must have navigation properties defined in both ends of the relationship.
This is the Student class:
There is a navigation property, which you can see in the line 9. This means that a Student can have only one course.
This is the Course class:
The navigation property for this class is an IEnumerable of Student because a course can have one or more students, so this needs to be a list.
On this case of a one-to-many relationship, the entity that has the collection navigation property, that in this case is the Course entity, is the principal entity, and the entity that has the reference navigation property is the dependent entity, which in this case is the Student.
DbContext
Now in the Dbcontext class, it’s necessary to override the method OnModelCreating. Inside this method, we are going to set the configuration for the entities using Fluent API.
For One-To-Many relationship, it’s possible to configure the relationship using both ends of the relationship. On the example below, I implemented in both ways to show as an example.
The Fluent API configuration we must add in the method “OnModelCreating” in the EFCoreRelationshipsExamplesDbContext class. For each kind of relationship, there is a separated method:
This is the method with the Fluent API configuration:
The “isRequired” is used to prevent the relationship from being optional.
For One-To-Many, it’s possible to configure using any of both ends of the relationship. On this example, it’s starting with Course, but could also start using the Student class:
modelBuilder.Entity<Student>()
.HasOne(s => s.Course)
.WithMany(c => c.Students)
Now we can create the migration and update the database. Migration is a way to update the database in an incrementally way. So always when you create a new entity class in the application, or when you change some existing entity, you need to generate a new migration and update the database with this migration.
Open the Package Manager Console and execute the commands:
Add-Migration AddedOneToManyRelationship
Update-Database
This is the command using the .NET Core CLI:
dotnet ef migrations add AddedOneToManyRelationship
dotnet ef database update
We can see the relationship between the tables in the SQL Server:
In the Program class, we have the seeding to add data in the tables in the database. On the main method there is the call to the seeding method:
This is the implementation of the seeding method:
Now we can run the application and check the data in the database:
Fully Defined Relationships
Another way to create the One-to-many relationship it’s by the fully defined relationships. The way of doing this is similar to the previous approach, but we also need to create an “Id” property that is related to the related object.
So for this first example, we need to have navigation properties defined on both ends of the relationship and a foreign key property defined in the dependent entity class.
For this example, we will have two entities: Customer and Order. For this scenario, one customer can have many orders, and one order can have only one customer.
This is the Order class:
This is the Customer class:
With fully defined relationships we do not need to add any configuration in Fluent API. So in the DbContext class we just need to add the DbSets:
public DbSet<Customer> Customers { get; set; }
public DbSet<Order> Orders { get; set; }
Now we can create the migration and we will have a similar relationship to the previous approach:
Add-Migration AddedOneToManyFullyDefinedRelationship
Update-Database
We can see the relationship between the tables in the SQL Server:
In the Program class, we need to add the call for the seeding method in the Main method:
And this is the seeding method:
Then we can run the application again and check the data in the database:
Many-to-Many (N:N)
For this Many-to-Many relationship, we will have two entities: the Movie and the Actor. One movie can have many actors, and one actor can have many movies.
EF Core does not support many-to-many relationships without using a third entity to represent the join table. So for this case, we need to have another entity, which will be the ActorMovie. On this entity, we are going to mapping two separated one-to-many relationships.
“Many-to-many relationships without an entity class to represent the join table are not yet supported in EF Core. However, you can represent a many-to-many relationship by including an entity class for the join table and mapping two separate one-to-many relationships.”
This is the Actor class:
This is the Movie class:
This is the ActorMovie class:
In the DbContext class, we are going to use the Fluent API to declare the relationship configuration in the DbContext class. In the method OnModelCreating, let’s add the call to the configuration method:
This is the configuration method:
On the method ManyToManyRelationshipConfiguration, we defined the property ActorId and Movie Id as the primary key of the ActorMovie table, and we also added the configuration defining that the table Actor can have many movies, and the table Movies can have many actors.
Now we can create the migration and update the database:
Add-Migration AddedManyToManyRelationship
Update-Database
We can see the relationship between the tables in the SQL Server:
In the Program class, in the Main method, we have the call to the seeding method:
This is the seeding method:
Then we can run the application again and check the data in the database:
One-to-one (1:1)
One to one relationships have a reference navigation property on both sides. They follow the same conventions as one-to-many relationships, but a unique index is introduced on the foreign key property to ensure only one dependent is related to each principal.
For this example, we will have two entities, the Author and the AuthorBiography. One Author can only have only one AuthorBiography, and one AuthorBiography can only belong to only one Author.
A one to one (or more usually a one to zero or one) relationship exists when only one row of data in the principal table is linked to zero or one row in a dependent table.
This is the Author entity:
This is the AuthorBiography entity:
In the DbContext class, we are going to use the Fluent API to declare the relationship configuration in the DbContext class. In the method OnModelCreating, let’s add the call to the configuration method:
This is the configuration method:
As you can see on the example above, it’s necessary to set the foreign key, using the “HasforeinKey”.
Now we can create the migration and update the database:
Add-Migration AddedOneToOneRelationship
Update-Database
We can see the relationship between the tables in the SQL Server:
In the Program class, in the Main method, we have the call to the seeding method:
This is the seeding method:
Then we can run the application again and check the data in the database:
Conclusion
These are examples of how we can create One-to-Many, One-to-One and Many-to-Many relationships between the entities using EF Core and Fluent API. Fluent API allows us to configure manually how the tables will be created in the database. You can check the code of this project here:
https://github.com/henriquesd/EFCoreRelationshipsExamples
If you like this solution, I kindly ask you to give a ⭐️ in the repository.
If you want to check the new possible way to create Many-to-Many Relationships using EF Core 5.0 (or a higher version), check the article about this subject by clicking here.
Thanks for reading!