With Entity Framework Core’s one-to-one relationship support, you can effortlessly express and manage relationships where each entity is uniquely tied to another entity, ensuring consistent data integrity. Let’s explore it in detail.
Imagine we have two classes: “Employee” and “EmployeeDetails”
public class Employee
{
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class EmployeeDetails
{
public int DetailsId { get; set; }
public int EmployeeId { get; set; }
public string Email { get; set; }
}
The Employee
class represents an employee and the EmployeeDetails
class represents the details of an employee. We will establish an one-to-one relationship between two classes. This one-to-one relationship allows you to store and retrieve additional details for each employee, such as their email address, by accessing the Details
property of the Employee
class. It ensures that each employee has a unique set of details, and the relationship is established through the common EmployeeId
property between the two classes.
Conventions
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public EmployeeDetails EmployeeDetails { get; set; }
}
public class EmployeeDetails
{
public int Id { get; set; }
public string Email { get; set; }
public int EmployeeId { get; set; }
public Employee Employee { get; set; }
}
EmployeeId property in EmployeeDetails class
: An integer property representing the identifier of the corresponding employee. It is identified as a foreing key.Employee property in EmployeeDetails class
: An instance of theEmployee
class representing the associated employee.EmployeeDetails property in Employee class
: An instance of theEmployeeDetails
class representing additional details of the employee.
Each Employee
object can have a single corresponding EmployeeDetails
object. The EmployeeDetails
object is accessed through the EmployeeDetails
property of the Employee
class.
Fluent API
In such cases where EF Core conventions cannot accommodate your requirements, Fluent API provides a way to explicitly configure the mapping, relationships, constraints, and other aspects of your entities, allowing you to tailor the behavior of EF Core to fit your specific needs.
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public EmployeeDetails EmployeeDetails { get; set; }
}
public class EmployeeDetails
{
public int Id { get; set; }
public string Email { get; set; }
public int Employee_Id { get; set; }
public Employee Employee { get; set; }
}
In your DbContext class, override the OnModelCreating
method and use Fluent API to configure the one-to-one relationship:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasOne(e => e.EmployeeDetails)
.WithOne(ed => ed.Employee)
.HasForeignKey<EmployeeDetails>(ed => ed.Employee_Id);
}
HasOne
specifies that theEmployee
entity has one relatedEmployeeDetails
entity.WithOne
specifies that theEmployeeDetails
entity has one relatedEmployee
entity.HasForeignKey
specifies the foreign key propertyEmployee_Id
in theEmployeeDetails
entity that links to the primary keyId
in theEmployee
entity.
Data Annotations
public class Employee
{
[Key]
public int EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public EmployeeDetails EmployeeDetails { get; set; }
}
public class EmployeeDetails
{
[Key]
public int EmployeeDetailsId { get; set; }
public string Email { get; set; }
public int EmployeeId { get; set; }
[ForeignKey("Employee")]
public Employee Employee { get; set; }
}
We use the [ForeignKey("Employee")]
attribute on the EmployeeId
property to specify the foreign key constraint, indicating that it references the Employee
navigation property in the EmployeeDetails
class.
Shared Primary Key Approach
When working with one-to-one relationships in Entity Framework Core (EF Core), one of several best practice approaches is shared primary key approach.
In some scenarios, you may want to use a shared primary key between the two entities in a one-to-one relationship. This means that the primary key of one entity is also used as the primary key and foreign key in the other entity. This can simplify the relationship and eliminate the need for an additional foreign key property.
Implementation using data annotations:
public class Employee
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public EmployeeDetails EmployeeDetails { get; set; }
}
public class EmployeeDetails
{
[Key,ForeignKey("Employee")]
public int Id { get; set; }
public string Email { get; set; }
public Employee Employee { get; set; }
}
Implementation using Fluent API:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public EmployeeDetails EmployeeDetails { get; set; }
}
public class EmployeeDetails
{
public int Id { get; set; }
public string Email { get; set; }
public Employee Employee { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasOne(e => e.EmployeeDetails)
.WithOne(ed => ed.Employee)
.HasForeignKey<EmployeeDetails>(ed => ed.Id);
// Additional configurations or entities...
base.OnModelCreating(modelBuilder);
}
You can check my other article.