MSDN Documentation

Managing Relationships with Entity Framework

Entity Framework (EF) simplifies the management of relationships between your domain entities, whether they are one-to-one, one-to-many, or many-to-many. EF uses conventions and explicit configuration to map these relationships to your database.

Types of Relationships

One-to-One Relationships

A one-to-one relationship exists when an instance of one entity is related to at most one instance of another entity, and vice versa.

For example, a UserProfile might have a one-to-one relationship with a UserDetails entity.


public class UserProfile
{
    public int UserId { get; set; }
    public string Username { get; set; }
    public UserDetails UserDetails { get; set; }
}

public class UserDetails
{
    public int UserId { get; set; } // Foreign key and primary key
    public string Bio { get; set; }
    public UserProfile UserProfile { get; set; }
}
            

In EF, you typically configure this by making the foreign key property also the primary key of the dependent entity, and then including the navigation properties.

One-to-Many Relationships

A one-to-many relationship exists when one instance of an entity can be related to multiple instances of another entity, but an instance of the second entity is related to at most one instance of the first.

A common example is a Department that can have many Employees.


public class Department
{
    public int DepartmentId { get; set; }
    public string Name { get; set; }
    public ICollection Employees { get; set; }
}

public class Employee
{
    public int EmployeeId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int DepartmentId { get; set; } // Foreign key
    public Department Department { get; set; }
}
            

EF typically infers this relationship from the presence of the foreign key property (DepartmentId) and the navigation properties (Department on Employee and Employees on Department).

Many-to-Many Relationships

A many-to-many relationship exists when one instance of an entity can be related to multiple instances of another entity, and vice versa.

Consider a scenario where a Student can enroll in many Courses, and a Course can have many Students.


public class Student
{
    public int StudentId { get; set; }
    public string Name { get; set; }
    public ICollection Courses { get; set; }
}

public class Course
{
    public int CourseId { get; set; }
    public string Title { get; set; }
    public ICollection Students { get; set; }
}
            

In the database, this is implemented using a joining table (also known as a linking or bridge table). EF automatically creates this joining table if you define the navigation properties correctly on both sides.

Configuring Relationships

While EF conventions are powerful, you can explicitly configure relationships using Data Annotations or the Fluent API (in the OnModelCreating method of your DbContext).

Using Data Annotations

You can use attributes like [ForeignKey] and [InverseProperty] to guide EF.


public class Employee
{
    public int EmployeeId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    [ForeignKey("Department")]
    public int DepartmentId { get; set; }
    public Department Department { get; set; }
}
            

Using the Fluent API

The Fluent API provides more granular control.


protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity()
        .HasMany(d => d.Employees)
        .WithOne(e => e.Department)
        .HasForeignKey(e => e.DepartmentId);

    modelBuilder.Entity()
        .HasMany(s => s.Courses)
        .WithMany(c => c.Students)
        .Map(cs => {
            cs.MapLeftKey("StudentId");
            cs.MapRightKey("CourseId");
            cs.ToTable("StudentCourses"); // Custom join table name
        });
}
            
Note: When using the Fluent API for many-to-many relationships, you can customize the joining table name and the foreign key column names.

Working with Relationships

Once relationships are configured, EF manages them transparently. You can load related entities using eager loading, explicit loading, or lazy loading.

Eager Loading

Load related data in the same query.


var department = _context.Departments
    .Include(d => d.Employees)
    .FirstOrDefault(d => d.DepartmentId == 1);
            

Explicit Loading

Load related data on demand after the principal entity has been loaded.


var department = _context.Departments.Find(1);
_context.Entry(department).Collection(d => d.Employees).Load();
            

Lazy Loading

Requires virtual navigation properties and EF Core tools.


// Assuming virtual navigation properties are configured
var department = _context.Departments.Find(1);
var employees = department.Employees; // This will trigger a separate query if not already loaded.
            
Tip: Eager loading is often preferred for performance when you know you'll need the related data, as it reduces the number of database round trips.
Warning: Be mindful of potential performance impacts with lazy loading, especially in loops, as it can lead to the "N+1 select problem."