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 propertyProfile
that is a reference navigation to theEmployeeProfile
class (child). - The
EmployeeProfile
has a required foreign key propertyEmployeeId
and reference navigation into theEmployee
(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.