EF Core One-to-many Relationships

Summary: in this tutorial, you’ll learn about one-to-many relationships and how to implement them in EF Core.

Introduction to one-to-many relationships

In the relational database, a one-to-many relationship is a common type of relationship between two tables where a row in a table can associate with multiple rows in another table.

For example, a department can have multiple employees while an employee belongs to one department. Therefore, the Departments has a one-to-many relationship with the Employees table.

The one-to-many relationship is also known as the parent-child relationship. The Departments is called a parent table while the Employees table is called a child table.

To establish the one-to-many relationship between the Departments and Employees tables, the Employees table needs to have a foreign key column called DepartmentId that references the Id column of the Employees table:

If the DepartmentId column accepts NULL, you can insert a row into the Employees table without specifying a corresponding row in the Departments table. In this case, the employee doesn’t belong to any department.

However, if the DepartmentId column doesn’t accept NULL, you need to use an Id from the Departments table for inserting a new row into the Employees table. In this case, an employee must belong to a specific department. In other words, you need to have at least one row in the Departments table first before you can insert rows into the Employees table.

We will demonstrate typical scenarios so that you can model one-to-many relationships between entities. For comprehensive scenarios, you can reference this page.

Modeling required one-to-many relationship

The following shows how to model a required one-to-many relationship between the Department and Employee entities:

public class Department
{
    public int Id { get; set; }
    public required string Name { get; set; }

    // Collection navigation containing children
    public ICollection<Employee> Employees { get; set; }
}

public class Employee
{
    public int Id { get; set; }
    public required string FirstName {  get; set; }
    public required string LastName {  get; set; }
    public required decimal Salary { get; set; }
    public required DateTime JoinedDate { get; set; }
    
    // Required foreign key property    
    public int DepartmentId  {   get; set; } 

    //  Required reference navigation to parent 
    public Department Department {  get; set; } = null!;
}Code language: C# (cs)

In this example:

  • The Department class has a property that is a collection of Employee objects.
  • The Employee class has two properties DepartmentId and Department. The DepartmentId is called a foreign key property, which is marked as required. This makes the one-to-many relationship required because each Employee must associate with at least a Department. The Department property is known as a navigation property.

Based on this model, EF Core generates the Departments and Employees table in the SQL Server database with the following structure:

CREATE TABLE [dbo].[Departments] (
    [Id]   INT            IDENTITY (1, 1) NOT NULL,
    [Name] NVARCHAR (MAX) NOT NULL,
    CONSTRAINT [PK_Departments] PRIMARY KEY CLUSTERED ([Id] ASC)
);

GO 
CREATE TABLE [dbo].[Employees] (CREATE TABLE [dbo].[Employees] (
    [Id]           INT             IDENTITY (1, 1) NOT NULL,
    [FirstName]    NVARCHAR (MAX)  NOT NULL,
    [LastName]     NVARCHAR (MAX)  NOT NULL,
    [Salary]       DECIMAL (18, 2) NOT NULL,
    [JoinedDate]   DATETIME2 (7)   NOT NULL,
    [DepartmentId] INT             NOT NULL,
    CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_Employees_Departments_DepartmentId] FOREIGN KEY ([DepartmentId]) REFERENCES [dbo].[Departments] ([Id]) ON DELETE CASCADE
);


GO
CREATE NONCLUSTERED INDEX [IX_Employees_DepartmentId]
    ON [dbo].[Employees]([DepartmentId] ASC);
Code language: SQL (Structured Query Language) (sql)

Note that the DepartmentId column in the Employees table is not NULL. Also, it is a foreign key that references the Id column of the Departments table.

Modeling the optional one-to-many relationship

The following changes the DepartmentId foreign key property and Departments navigation property of the Employee class to nullable:

public class Department
{
    public int Id { get; set; }
    public required string Name { get; set; }
    public ICollection<Employee> Employees { get; set; }
}
public class Employee
{
    public int Id { get; set; }
    public required string FirstName {  get; set; }
    public required string LastName {  get; set; }
    public required DateTime JoinedDate { get; set; }
    
    public int? DepartmentId  {   get;set; }
    public Department? Department {  get; set; }
}Code language: C# (cs)

Since the DepartmentId and Department properties are nullable, you can create an Employee object without a Department.

EF Core creates the following Departments and Employees tables in the database. Notice that the DepartmentId in the Employees table accepts NULL:

CREATE TABLE [dbo].[Departments] (
    [Id]   INT            IDENTITY (1, 1) NOT NULL,
    [Name] NVARCHAR (MAX) NOT NULL,
    CONSTRAINT [PK_Departments] PRIMARY KEY CLUSTERED ([Id] ASC)
);

CREATE TABLE [dbo].[Employees] (
    [Id]           INT            IDENTITY (1, 1) NOT NULL,
    [FirstName]    NVARCHAR (MAX) NOT NULL,
    [LastName]     NVARCHAR (MAX) NOT NULL,
    [JoinedDate]   DATETIME2 (7)  NOT NULL,
    [DepartmentId] INT            NULL,
    CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_Employees_Departments_DepartmentId] FOREIGN KEY ([DepartmentId]) REFERENCES [dbo].[Departments] ([Id])
);


GO
CREATE NONCLUSTERED INDEX [IX_Employees_DepartmentId]
    ON [dbo].[Employees]([DepartmentId] ASC);

Code language: SQL (Structured Query Language) (sql)

Summary

  • EF Core uses conventions to infer the one-to-many relationships between model classes and create the corresponding tables in the database.
Was this tutorial helpful ?