One-to-Many Relationships in Entity Framework Core

One-to-Many Relationships in Entity Framework Core using Fluent API

In this article, I will discuss How to Configure One-to-Many Relationships between two Entities in Entity Framework Core using Fluent API with Examples. Please read our previous article, which discusses How to Configure One-to-One Relationships between two entities in EF Core using Fluent API.

One-to-Many Relationships in Entity Framework Core using Fluent API

The One-To-Many relationship is the most common type of relationship between database tables or entities. In this type of relationship, a row in one table, let’s say Table A, can have many matching rows in another table, let’s say Table B, but a row in Table B can have only one matching row in Table A.

In Entity Framework Core (EF Core), a one-to-many (1:N) relationship means that one entity (often termed the “principal” or “parent”) can be related to multiple instances of another entity (often termed the “dependent” or “child”), but each instance of the dependent entity relates back to only one instance of the principal entity.

For a one-to-many relationship, the principal entity typically has a collection navigation property, while the dependent entity will have a reference navigation property and a foreign key property. To represent a one-to-many relationship, we will typically have:

  • A collection navigation property in the principal entity points to a collection of dependent entities.
  • A foreign key property in the dependent entity pointing back to the principal entity.
  • A reference navigation property in the dependent entity pointing back to the principal entity.
Examples of Understanding One-to-Many Relationships in EF Core

In Entity Framework Core (EF Core), you can define a one-to-many relationship between entities using the Fluent API. The Fluent API provides a way to configure the relationships and mappings between entities in a more flexible and detailed manner than using only data annotations.

Let us understand this with an example. We will implement a One-to-Many relationship between the following Author and Book Entities. Consider the following Author and Book classes where the Author entity includes many Book entities, but each book entity is associated with only one Author entity. So, an Author can write multiple Books, but each Book has only one Author.

Author.cs

First, create a class file named Author.cs, and copy and paste the following code. As you can see, this entity has a few scaler properties and one collection navigation property pointing to the Book entity. This will be our Principal Entity.

namespace EFCoreCodeFirstDemo.Entities
{
    public class Author
    {
        public int AuthorId { get; set; }
        public string? Name { get; set; }
        // Navigation property: Collection pointing to dependent entities
        public List<Book>? Books { get; set; }
    }
}

Book.cs

Next, create another class file named Book.cs, and copy and paste the following code. As you can see, this entity has a few scaler properties and one reference navigation property pointing to the Author entity. AuthorId is going to be the Foreign Key. This will be our Dependent Entity.

namespace EFCoreCodeFirstDemo.Entities
{
    public class Book
    {
        public int BookId { get; set; }
        public string? Title { get; set; }
        // Foreign key property
        public int AuthorId { get; set; }
        // Navigation property: Points back to the principal entity
        public Author? Author { get; set; }
    }
}

How to Configure One-to-Many Relationships Using Fluent API in EF Core?

Generally, we do not need to configure the one-to-many relationship in the entity framework core because one-to-many relationship conventions cover all combinations. However, you may configure relationships using Fluent API in one place to make them more maintainable.

Now, you can configure the one-to-many relationship for the above two entities in Entity Framework Core using Fluent API by overriding the OnModelCreating method in the context class. To do so, we will use the following Fluent API methods.

  1. HasMany(): Configures a relationship where this entity type has a collection that contains instances of the other type in the relationship. That means it specifies the ‘many’ side of the relationship. 
  2. WithOne(): Configures a relationship where this entity type has a reference that contains an instance of the other type in the relationship. That means it specifies the ‘one’ side of the relationship. 
  3. HasForeignKey(): Configures the property(s) to use as the foreign key for this relationship. That means it specifies which property is the foreign key.

Now, modify the context class as follows. In this example, the Author is the Principal Entity, and the Book is the dependent Entity. One Author can have multiple books, and each book can belong to a single author. So, in the Book Entity, we will create the Foreign Key. 

using Microsoft.EntityFrameworkCore;
namespace EFCoreCodeFirstDemo.Entities
{
    public class EFCoreDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //Configuring the Connection String
            optionsBuilder.UseSqlServer(@"Server=LAPTOP-6P5NK25R\SQLSERVER2022DEV;Database=EFCoreDB;Trusted_Connection=True;TrustServerCertificate=True;");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Fluent API Configuration
            // Configure One to Many Relationships Between Author and Book
            modelBuilder.Entity<Author>()
            .HasMany(a => a.Books) // Author has many Books, specifies the 'many' side of the relationship
            .WithOne(b => b.Author) // Book is associated with one Author, specifies the 'one' side of the relationship
            .HasForeignKey(b => b.AuthorId); // AuthorId is the Foreign key in Book table, specifies which property is the foreign key
        }

        public DbSet<Author> Authors { get; set; }
        public DbSet<Book> Books { get; set; }
    }
}

In the context class, we need to configure the relationship using EF Core Fluent API within the OnModelCreating method. The following code configures the One-to-Many relationship between the Author and Book entities. That means one Author can have multiple books (0 or 1, n number of books), and one book can belong to a single Author.

One-to-Many Relationships in Entity Framework Core

Code Explanation:
  • modelBuilder.Entity<Author>(): First, we must start configuring with one entity class, either Author or Book. So, here, we start configuring the Author entity.
  • HasMany(a => a.Books): Specifies the ‘many’ side of the relationship. This method specifies that the Author entity includes one Books collection navigation property using a lambda expression. It configures a relationship where this entity type, i.e., Author entity, has a collection property, i.e., Books, that points to instances of the other entity, i.e., Book, in the relationship. That means the Author has many Books.
  • WithOne(b => b.Author): Specifies the ‘one’ side of the relationship. This method configures the other end of the relationship, the Book. It specifies that the Book entity includes a reference navigation property of Author type, i.e., Book entity, has a reference property, i.e., Author, that points to an instance of the other entity, i.e., Author, in the relationship. That means the one Book is associated with only one Author.
  • HasForeignKey(b => b.AuthorId): Specifies which property is the foreign key. This method specifies the foreign key property name. It configures the property, i.e., AuthorId, as the foreign key for this relationship. That means AuthorId is the Foreign key in the Book table.

Note: Before Proceeding further, let us delete the EFCoreDB database using SSMS and Migration folder from our project.

With the above changes, open the Package Manager Console and Execute the add-migration and update-database commands as follows. You can give any name to your migration. Here, I am giving EFCoreDBMig1. The name that you are giving it should not be given earlier.

How to Configure One-to-Many Relationships between two Entities in Entity Framework Core using Fluent API with Examples

Now, if you verify the database, you should see the following.

How to Configure One-to-Many Relationships between two Entities in Entity Framework Core using Fluent API

Next, modify the Main method of the Program class as follows:

using EFCoreCodeFirstDemo.Entities;
namespace EFCoreCodeFirstDemo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            try
            {
                using var context = new EFCoreDbContext();

                //First Create Book Collection
                var books = new List<Book>
                {
                    new Book() { Title = "C#" },
                    new Book() { Title = "LINQ" },
                    new Book() { Title = "ASP.NET Core" }
                };

                //Create an Instance of Author and Assign the books collection
                var author = new Author() { Name="Pranaya", Books = books };
                context.Authors.Add(author);
                context.SaveChanges();
                
                Console.WriteLine("Author and Books Added");

                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}"); ;
            }
        }
    }
}

Execute the above code and then verify the database. You will see one Author record with three corresponding books, as shown in the image below.

How to Configure One-to-Many Relationships in Entity Framework Core using Fluent API

How to Configure Cascade Delete using EF Core Fluent API?

In Entity Framework Core (EF Core), the OnDelete method configures delete behavior for related entities when the principal entity is deleted. This behavior is often described in terms of cascading actions.

When defining relationships between entities, especially with foreign keys, it’s important to determine what should happen when a record referenced by another record is deleted. The OnDelete method allows you to specify this behavior.

Cascade Delete means automatically deleting the child rows when the related parent row is deleted. For example, if the Author is deleted from the Authors table, then automatically, all the books belonging to that Author should also be deleted from the Books table. To implement this, we need to use the OnDelete Fluent API Method. So, modify the EFCoreDbContext class as follows. Here, you can see we are using the OnDelete method, and to this method, we are passing DeleteBehavior.Cascade enum named constant.

using Microsoft.EntityFrameworkCore;
namespace EFCoreCodeFirstDemo.Entities
{
    public class EFCoreDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            //Configuring the Connection String
            optionsBuilder.UseSqlServer(@"Server=LAPTOP-6P5NK25R\SQLSERVER2022DEV;Database=EFCoreDB;Trusted_Connection=True;TrustServerCertificate=True;");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Fluent API Configuration
            // Configure One to Many Relationships Between Author and Book
            modelBuilder.Entity<Author>()
            .HasMany(a => a.Books) // Author has many Books, specifies the 'many' side of the relationship
            .WithOne(b => b.Author) // Book is associated with one Author, specifies the 'one' side of the relationship
            .HasForeignKey(b => b.AuthorId) // AuthorId is the Foreign key in Book table, specifies which property is the foreign key
            .OnDelete(DeleteBehavior.Cascade); //This will delete the child record(S) when parent record is deleted
        }

        public DbSet<Author> Authors { get; set; }
        public DbSet<Book> Books { get; set; }
    }
}

The OnDelete() of the Fluent API method uses the DeleteBehavior parameter. If you go to the definition of DeleteBehavior, you will see it is an enum with the following values. DeleteBehavior Indicates how a delete operation is applied to dependent entities in a relationship when the principal is deleted, or the relationship is severed.

How to Configure One-to-Many Relationships in EF Core using Fluent API

You can specify any of the above DeleteBehavior values based on your requirements. The meaning of the above enum named constants are as follows:

  1. Cascade: For entities being tracked by the context, dependent entities will be deleted when the related principal is deleted.
  2. ClientSetNull: For entities being tracked by the context, the values of foreign key properties in dependent entities are set to null when the related principal is deleted. If a property cannot be set to null because it is not a nullable type, an exception will be thrown when SaveChanges() is called.
  3. Restrict: For entities being tracked by the context, it prevents the principal entity from being deleted if any related dependents exist. This is useful to prevent accidental data loss.
  4. SetNull: For entities being tracked by the context, the values of foreign key properties in dependent entities are set to null when the related principal is deleted. If a property cannot be set to null because it is not a nullable type, an exception will be thrown when SaveChanges() is called.
  5. ClientCascade: For entities being tracked by the context, dependent entities will be deleted when the related principal is deleted.
  6. NoAction: For entities being tracked by the context, the values of foreign key properties in dependent entities are set to null when the related principal is deleted. If a property cannot be set to null because it is not a nullable type, an exception will be thrown when SaveChanges() is called.
  7. ClientNoAction: It is unusual to use this value. Consider using DeleteBehavior.ClientSetNull instead to match the behavior of EF6 with cascading deletes disabled.

With the above changes, open the Package Manager Console and Execute the add-migration and update-database commands as follows. You can give any name to your migration. Here, I am giving EFCoreDBMig3. The name that you are giving it should not be given earlier.

Configure One-to-Many Relationships in EF Core using Fluent API

Currently, we have one author in the Authors database table with the AuthorId 1, and it has three corresponding records in the Books database table, as shown in the image below.

One-to-Many Relationships in EF Core using Fluent API

Modifying the Main Method:

Now, let us modify the Main method as follows. Here, we are deleting the Principal Author Entity whose ID is 1. And then, we will verify whether the corresponding Book entities are going to be deleted or not.

using EFCoreCodeFirstDemo.Entities;
namespace EFCoreCodeFirstDemo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            try
            {
                using var context = new EFCoreDbContext();

                Author? Id1 = context.Authors.Find(1);
                if (Id1 != null)
                {
                    context.Remove(Id1);
                    context.SaveChanges();
                    Console.WriteLine("Author Deleted");
                }
                else
                {
                    Console.WriteLine("Author Not Exists");
                }
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}"); ;
            }
        }
    }
}

Once you execute the application code, verify the database and check the Books table, and you will see the corresponding Author records should have been deleted.

Points to Remember:
  1. Database Provider Limitations: Not all delete behaviors are supported by all database providers. It’s essential to check the capabilities of your database provider when choosing a delete behavior.
  2. Data Integrity: Cascading deletes can lead to unintentional data loss. Always ensure you understand the implications of your chosen delete behavior, especially in production scenarios.
  3. Database-Level vs. Application-Level: It’s important to note that while EF Core can handle cascading actions at the application level, it’s often recommended to have these actions defined at the database level for performance and data integrity. When you use OnDelete, EF Core will try to configure the behavior at the database level, but it’s essential to know how your database handles these actions.
How to Implement Update Behavior to Dependent Entities in EF Core using Fluent API?

In Entity Framework Core (EF Core), when dealing with related entities, you often need to determine the behavior for updates, especially when a principal entity’s primary key (often used as a foreign key in the dependent entity) changes. This is less common than the delete behavior scenarios, mainly because primary keys are typically stable and don’t change often. However, it’s important to understand how updates work in relationships, particularly for non-primary key properties.

For Relationships in EF Core:

  • Non-Primary Key Property Updates: By default, when you modify properties of a principal entity that are not part of the primary key, and those properties are involved in a relationship (e.g., used as a foreign key in a dependent entity), the changes won’t cascade to the dependent entities automatically. You will need to handle such updates manually in your application logic.
  • Primary Key Property Updates: Changing primary keys is generally not suggested because a primary key’s purpose is to serve as a unique identifier. If you need to change primary keys, it often indicates a design issue in the schema. EF Core does not support changing the value of primary keys for tracked entities out of the box. In such cases, you need to handle this with custom logic, such as deleting the old record and creating a new one, then updating any dependents manually.

In the next article, I will discuss How to Configure Many-to-Many Relationships in Entity Framework Core using Fluent API with Examples. In this article, I explain How to Configure One-to-Many Relationships in Entity Framework Core using Fluent API with Examples. I hope you enjoyed this Configure One-to-Many Relationship in EF Core using Fluent API article.

Leave a Reply

Your email address will not be published. Required fields are marked *