How to Configure Entity Relationships using Fluent Api in Entity Framework Core

by GeeksArray

This blogpost explains how you can configure One-To-One, One-to-Many, Many-To-Many entity relationships using Fluent API in Entity Framework Core. Configuring these relationships between entities are important for the implementation of code first approach.

Fluent API provides HasRequired, WithOptional, HasRequired, HasMany, WithRequiredPrincipal, WithMany method to configure different relationships between entities.

You can generate Models if you already have database through Entity Framework Core Database First tutorial. This tutorial uses the Northwind database and generates Model classes. You can implement following configuration using these Northwind Model classes.

  1. Configure One-To-One Relationship

    By convention method

    By default, EF Core will create a One-To-One relationship if reference navigation property has set on principal as well as dependent entity. The principal entity can have zero-or-one association with the dependent entity. Dependent entity can have only one association with Principal entity.

    The following code has a navigation property between Employee and EmployeeAddress entity. EF Core automatically creates a foreign key property as EmployeeID in the EmployeeAddress entity.

    public class Employee
    {
        public int EmployeeId { get; set; }
        public string Name { get; set; }
           
        public EmployeeAddress Address { get; set; }
    }
    
    public class EmployeeAddress
    {
        public int EmployeeAddressId { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Country { get; set; }
    
        public int EmployeeId { get; set; }
        public Employee Employee { get; set; }
    }
    

    By convention Entity Framework Core will create One-To-One relationship between Employee and EmployeeAddress tables as Principal entity Employee has reference navigation property for EmployeeAddress entity. And EmployeeAddress has foreign key EmployeeId and reference navigation property Employee.

    Configure One-To-One relationship using Fluent API

    When Key or ForeignKey properties do not follow conventions you can use Data Annotation or Fluent API to configure relationships between entities.

    See the following code I have change EmployeeAddress entity for EmpId property. If I use EmployeeId as column name then by convention EF Core will create foreign key.

    public class Employee
    {
        public int EmployeeId { get; set; }
        public string Name { get; set; }
           
        public EmployeeAddress Address { get; set; }
    }
    
    public class EmployeeAddress
    {
        public int EmployeeAddressId { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Country { get; set; }
    
        public int EmpId { get; set; }
        public Employee Employee { get; set; }
    }
    
    

    You can use the following code in DbContext class to override OnModelCreating method and configure the relationship using Fluent API.

    using EF_Core_Fluent_API_Relationship.Entity;
    using Microsoft.EntityFrameworkCore;
    
    namespace EF_Core_Fluent_API_Relationship
    {
    public class StoreContext : DbContext
    {
    public StoreContext(DbContextOptions options) : base(options)
    {
    
    }
            
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(connectionString);
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Employee>()
            .HasOne<EmployeeAddress>(e => e.Address)
            .WithOne(em => em.Employee)
            .HasForeignKey<EmployeeAddress>(em => em.EmpId);
    }
    
    public DbSet<Employee> Employees { get; set; }
    public DbSet<EmployeeAddress> Addresses { get; set; }
    }
    }
    

    • Lambda expression .HasOne<EmployeeAddress>(e => e.Address) configures Emoloyee entity will have only one EmployeeAddress reference navigation property.
    • .WithOne(em => em.Employee) configures reference navigation property for EmployeeAddress to include Employee entity.
    • .HasForeignKey<EmployeeAddress>(em => em.EmpId); configures foreign key of EmployeeID column from Employee Entity to EmployeeAddress entity

    Employee And EmployeeAddress entities are ready with One-To-One relationship. Create your database and tables using Add-Migration. It creates tables as shown.

    aspnet core web api new project

  2. Configure One-To-Many Relationship

    Convention

    When you include reference navigation property only independent entity, Entity Framework Core will create a One-To-Many relationship. Following code creates One-To-Many relationship between Employee and EmployeeAddress entities. One EmployeeAddress can be associated with many Employees.

    public class Employee
    {
        public int EmployeeId { get; set; }
        public string Name { get; set; }
            
        public int EmployeeAddressId { get; set; }
        public EmployeeAddress EmployeeAddress { get; set; }
    }
    
    public class EmployeeAddress
    {
         public int EmployeeAddressId { get; set; }
         public string Street { get; set; }
         public string City { get; set; }
         
         public List<Employee> Employees { get; set; }       
    }
    

    Another convention when you include a collection navigation property in principal entity Entity Framework Core will create a One-To-Many relationship.

    Configure One-To-Many relationship using Fluent API

    Configure One-To-Many relationship using Fluent API In the following example, the Employee entity includes a list of EmployeeAddress so one EmployeeAddress(Parent) can be associated with many Employees(Child). It creates a relationship using the Fluent API. One or more family members can be employees of the same organization.

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<EmployeeAddress>()
            .HasOne<Employee>(e => e.Employee)
            .WithMany(g => g.EmployeeAddress)
            .HasForeignKey(s => s.EmployeeId);
    }
    

    This generates tables as shown below

    fluent api one to many relationship

    Notice it created a Non-Unique index for the EmployeeAddressId column of the Employee table.

    Configure Cascade behavior using OnDelete of Fluent API Chaining.

    Cascade delete is always a unidirectional operation from Principal entity to dependent entity, which means deleting Principal entity will result in deletion of the dependent entity.

    The OnDelete method takes DeleteBehavior enum value as an input parameter.

    • DeleteBehavior.Cascade: Child/Dependent entity should be deleted when Parent/Principal entity is deleted. If you have a non-nullable foreign key relationship this behavior is the default. If you have child entities that can not exist without having parent DeleteBehavior.Cascade will take care of deleting child entities when the parent entity is deleted. Order and OrderDetails is the best example of it.
    • DeleteBehavior.ClientSetNull: Foreign key properties value set to NULL. If you have a nullable foreign key relationship this behavior is the default. When child entities may or may not have parent entity associated you can use DeleteBehavior.ClientSetNull. Employee (Parent) and Bonus(Child) is an example of it.
    • DeleteBehavior.Restrict: The deletion of parent entity does not have any effect on associated child entities. Orders(Parent) and Customer(Child) is the example of it.
    • DeleteBehavior.SetNull: foreign key values of dependent rows should update to NULL when the associated parent entity is deleted. Department(Parent) and Employee(Child) is example of it.

    Following code sets DeleteBehavior.Restrict on EmployeeAddress and Employee relationship. So whenever EmployeeAddress is deleted there will be no effect on Employee entity.

    
    modelBuilder.Entity<Employee>()
        .HasOne<EmployeeAddress>(e => e.EmployeeAddress)
        .WithMany(g => g.Employees)
        .HasForeignKey(s => s.EmployeeAddressId).OnDelete(DeleteBehavior.Restrict);
    
  3. Configure Many-To-Many relationship

    Convention

    Entity Framework Core does not have a convention for Many-To-Many relationships. You will have to use the Fluent API.

    Configure One-To-Many relationship using Fluent API

    To create One-To-Many relationship in EF Core you will have to create a linking entity class that links two different entities and then add navigation properties to either side of the many-to-many relations that point to the join entity instead.

    The linking entity will have separate One-To-Many relationship with two different linked entities.

    For example, One Employee can work for many departments and one Department will have many Employees, to configure this relationship we will have to have a linking entity like DeptEmployee. Linking entity which is DeptEmployee will have One-To-Many relationship with the Department as well as Employee entity.

    The following code creates an Employee, Department, DeptEmployee entities.

    public class Employee
    {
        public int EmployeeId { get; set; }
        public string Name { get; set; }    
        public virtual ICollection<DeptEmployee> DeptEmployee { get; set; } 
    }
    
    public class Department 
    {
        public int DepartmentID { get; set;}
        public string DeptName {get; set;}
        public virtual ICollection<DeptEmployee> DeptEmployee { get; set; }
    }
    
    public class DeptEmployee
    {
        public int DeptEmpID {get; set;}
      
        public int EmployeeId { get; set; }
        public Employee Employee { get; set; }
        public int DepartmentId { get; set; }
        public Department Department { get; set; } 
    }
    

    Update your OnModelCreating method and DBContext class as shown below.

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<DeptEmployee>()
                .HasKey(de => new { de.EmployeeId, de.DepartmentId });
    }
    
    public DbSet<Employee> Employees { get; set; }
            
    public DbSet<Department> Departments { get; set; }
    public DbSet<DeptEmployee> DeptEmployees { get; set; }
    

    Now you can use Add-Migration and Update-Database -verbose to create the database. It generates below table schema and database.

    many to many relationship using fluent api entity frameworkcore

Source code on Git hub Source Code on Github

Speak your mind
Please login to post your comment!