EF Core One-to-one Relationships

Summary: in this tutorial, you will learn about EF Core one-to-one relationships and how to model them properly.

Introduction to EF Core one-to-one relationships

In a relational database, a one-to-one relationship allows you to link two tables so that a row in one table is associated with at most a row in another table.

For example, an employee can have a profile containing phone and email. The reason we don’t put these fields in the same Employees table is that we may rarely use the phone and email when we work with Employee objects.

In the database, we’ll have two separate tables Employees and EmployeeProfiles. The EmployeeProfiles table will have a foreign key EmployeeId that references the Id of the Emloyees table:

Based on this database diagram, a row in the Employees table can be associated with one or many rows in the EmployeeProfiles table.

To enforce the one-to-one relationship, the values in the EmployeeId column of the EmployeeProfiles need to be unique. To do that, you can create a unique index (or constraint) that includes the EmployeeId.

Modeling one-to-one relationships in EF Core

The following classes illustrate how to model a one-to-one relationship between the Employee and EmployeeProfile entities:

namespace HR;


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; }
    public int DepartmentId  {   get; set; }

    // Reference navigation to Department
    public Department Department { get; set; } = null!;

    // Reference navigation to EmployeeProfile
    public EmployeeProfile? Profile  { get; set; }
}

public class EmployeeProfile
{
    public int Id { get; set;}
    public string Phone  { get;set;   }
    public string Email {   get;set;  }

    // Required foreign key property
    public int EmployeeId { get; set;}
    
    // Required reference navigation to Employee
    public Employee Employee  { get; set;} = null!;
}Code language: C# (cs)

In these classes:

  • The Employee class has a property Profile that is a reference navigation to the EmployeeProfile class (child).
  • The EmployeeProfile has a required foreign key property EmployeeId and reference navigation into the Employee (parent).

Based on these classes and DbSet defined in the HRContext class EF Core generates the following tables in SQL Sever Database:

Emloyees table:

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)

EmployeeProfiles table:

CREATE TABLE [dbo].[EmployeeProfiles] (
    [Id]         INT            IDENTITY (1, 1) NOT NULL,
    [Phone]      NVARCHAR (MAX) NOT NULL,
    [Email]      NVARCHAR (MAX) NOT NULL,
    [EmployeeId] INT            NOT NULL,
    CONSTRAINT [PK_EmployeeProfiles] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [FK_EmployeeProfiles_Employees_EmployeeId] 
    FOREIGN KEY ([EmployeeId]) 
    REFERENCES [dbo].[Employees] ([Id]) ON DELETE CASCADE
);


GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_EmployeeProfiles_EmployeeId]
    ON [dbo].[EmployeeProfiles]([EmployeeId] ASC);
Code language: SQL (Structured Query Language) (sql)

EF Core creates a unique index on the EmployeeId column of the EmployeeProfiles table to ensure that one row in the Employees table can be associated with at most one row in the EmloyeeProfiles table.

EF Core also sets the ON DELETE CASCADE on the foreign key EmployeeId in the EmployeeProfile table. Therefore, if you delete a row from the Employees table, the corresponding row in the EmployeeProfile is also deleted.

Summary

  • One-to-one relationships allow one entity to be associated with at most one other entity.
  • Use navigation properties in both classes with a foreign key in the child class to model a one-to-one relationship EF Core.
Was this tutorial helpful ?