One-to-Many Relationships in Entity Framework Core

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

In this article, I am going to 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, where we discussed 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 TableA, can have many matching rows in another table, let’s say TableB, but a row in TableB can have only one matching row in TableA.

This type of relationship is established when one entity has a collection navigation property pointing to a reference navigation property. The related entity often has a reference navigation property pointing back to the single entity (the “one” side). The [ForeignKey] attribute or fluent API can specify the related entity’s foreign key property. We have already discussed using the [ForeignKey] attribute, and in this article, I am going to discuss How to Implement One-to-Many using Fluent API.

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 are going to 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.

Author.cs

First, create a class file with the name Author.cs and then copy and paste the following code into it.

namespace EFCoreCodeFirstDemo.Entities
{
    public class Author
    {
        public int AuthorId { get; set; }
        public string? Name { get; set; }
        public List<Book>? Books { get; set; }
    }
}

Book.cs

Next, create another class file with the name Book.cs and copy and paste the following code.

namespace EFCoreCodeFirstDemo.Entities
{
    public class Book
    {
        public int BookId { get; set; }
        public string? Title { get; set; }
        public int AuthorId { get; set; }
        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 are going to 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.
  2. WithOne(): Configures this as a one-to-many relationship.
  3. HasForeignKey(): Configures the property(s) to use as the foreign key for this relationship.
  4. IsRequired(): Configures whether this is a required relationship (i.e., whether the foreign key property(s) can be assigned.

Now, modify the context class as follows:

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)
        {
            // Configure One to Many Relationships Between Author and Book
            modelBuilder.Entity<Author>()
            .HasMany(a => a.Books) // Author has many Books
            .WithOne(b => b.Author) // Book is associated with one Author
            .HasForeignKey(b => b.AuthorId) // AuthorId is the Foreign key in Book table
            .IsRequired(); //This is a Required Relationship
        }

        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.

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

Let’s understand the above code step by step.

  • modelBuilder.Entity<Author>(): First, we need to start configuring with one entity class, either Author or Book. So, here, we start configuring the Author entity.
  • HasMany(a => a.Books): 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 an instance of the other entity, i.e., Book, in the relationship. That means Author has many Books.
  • WithOne(b => b.Author): This method configures the other end of the relationship, the Book entity. It specifies that the Book entity includes a reference navigation property of Author type. Configures this as a one-to-many relationship. That means Book is associated with one Author.
  • HasForeignKey(b => b.AuthorId): This method specifies the foreign key property name. It configures the property, i.e., AuthorId, to use as the foreign key for this relationship. That means AuthorId is the Foreign key in the Book table.
  • IsRequired(): It configures whether this is a required relationship, i.e., whether the foreign key property(s) can be assigned. By default, its value is true. We can also set this value to false if we want to make the relationship optional. Here, by default, it will make the relationship required.

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

With the above changes in place, open Package Manager Console and Execute the following add-migration and update-database commands. 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}"); ;
            }
        }
    }
}

Now, if you 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

Alternatively, you can start configuring the relationship with the Book entity instead of the Author entity, as shown below.

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

Let’s understand the above code step by step.

  • First, we need to start configuring with one entity class, either Author or Book. So, modelBuilder.Entity<Book>() starts configuring with the Book entity.
  • Then, HasOne(b => b.Author) specifies that the Book entity includes an Author type property named Author.
  • Now, we need to configure the other end of the relationship, the Author entity. The WithMany(a => a.Books) specifies that the Author entity class includes many Books entities. Here, WithMany infers collection navigation property.
  • The HasForeignKey(a => a.AuthorId) specifies the name of the foreign key property AuthorId in the Book table.
  • Finally, IsRequired(true) configures this as a required relationship.

So, modify the context class as follows:

using Microsoft.EntityFrameworkCore;
using System.Diagnostics;

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)
        {
            // Configure One to Many Relationships Between Author and Book
            modelBuilder.Entity<Book>()
            .HasOne(b => b.Author) // Each Book Belongs to an Author
            .WithMany(a => a.Books) // Author is associated with many books
            .HasForeignKey(a => a.AuthorId) // AuthorId is the Foreign key in Book table
            .IsRequired(true); //This is a Required Relationship
        }

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

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

How to Configure One-to-Many Relationships Between two Entities in EF 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 EF Core using Fluent API with Examples

If you want to make the relationship optional, then you need to pass the value false to the IsRequired method, as shown below.

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

How to Configure Cascade Delete using Fluent API?

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 OnDelete Fluent API Method. Modify the context class as follows.

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)
        {
            // Configure One to Many Relationships Between Author and Book
            modelBuilder.Entity<Author>()
            .HasMany(a => a.Books) // Author has many Books
            .WithOne(b => b.Author) // Book is associated with one Author
            .HasForeignKey(b => b.AuthorId) // AuthorId is the Foreign key in Book table
            .IsRequired()
            .OnDelete(DeleteBehavior.Cascade); ; //This is a Required Relationship
        }

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

The OnDelete() method uses the DeleteBehavior parameter. If you go to the definition of DeleteBehavior, then you will see it is an enum with the following values.

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

You can specify any of the following DeleteBehavior values based on your requirement.

  1. Cascade: Dependent entities will be deleted when the principal entity is deleted.
  2. ClientSetNull: The values of foreign key properties in the dependent entities will be set to null. This can only be applied if the foreign key column accepts a null value.
  3. Restrict: Prevents Cascade delete.
  4. SetNull: The values of foreign key properties in the dependent entities will be set to null.
  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.
  7. ClientNoAction: For entities being tracked by the context, the values of foreign key properties in dependent entities are not changed when the related principal entity is deleted.

With the above changes in place, open Package Manager Console and Execute the following add-migration and update-database commands. 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

Modifying the Main Method:

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 below image.

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

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, then verify the database and check the Books table, and you will see the corresponding Author records should have been deleted.

In the next article, I am going to 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 *