Entity Framework Core Relationships

This document covers how to define and manage relationships between entities in Entity Framework Core (EF Core).

EF Core allows you to represent relationships between your domain entities. These relationships can be one-to-one, one-to-many, or many-to-many. EF Core uses conventions and configuration to map these relationships to your database schema.

One-to-Many Relationships

A one-to-many relationship occurs when one entity can be associated with multiple other entities, but each of those other entities can only be associated with one of the first entity. A common example is a Blog having many Posts.

To define a one-to-many relationship, you typically include a collection navigation property on the "one" side and a single reference navigation property on the "many" side. EF Core will infer this relationship.


public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    // Navigation property for the "many" side
    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    // Foreign key property
    public int BlogId { get; set; }
    // Navigation property for the "one" side
    public Blog Blog { get; set; }
}
            

In this example, Posts on the Blog entity represents the "many" side, and Blog on the Post entity represents the "one" side. The BlogId property in Post is the foreign key.

One-to-One Relationships

A one-to-one relationship exists when each instance of an entity can be associated with at most one instance of another entity, and vice-versa. For example, a UserProfile might have a single User.

One-to-one relationships can be implemented in a few ways:

Example of the principal entity having a foreign key:


public class User
{
    public int UserId { get; set; }
    public string Username { get; set; }

    // Navigation property for the dependent entity
    public UserProfile UserProfile { get; set; }
}

public class UserProfile
{
    public int UserProfileId { get; set; } // Can also be UserId if using shared PK
    public string Bio { get; set; }
    public DateTime DateOfBirth { get; set; }

    // Foreign key property, also the primary key in this configuration
    public int UserId { get; set; }
    // Navigation property for the principal entity
    public User User { get; set; }
}
            

Many-to-Many Relationships

A many-to-many relationship occurs when an entity can be associated with multiple other entities, and each of those other entities can also be associated with multiple of the first entity. A classic example is Student and Course, where a student can enroll in many courses, and a course can have many students.

Many-to-many relationships in relational databases are typically implemented using a junction table (also known as a linking table or join table). EF Core can automatically create this junction table for you.


public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }

    // Navigation properties for many-to-many
    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public int TagId { get; set; }
    public string Name { get; set; }

    // Navigation properties for many-to-many
    public ICollection<Post> Posts { get; set; }
}
            

When you configure these entities in DbContext, EF Core will automatically create a junction table (e.g., PostTag) with foreign keys to both PostId and TagId.

Foreign Key Attributes

While EF Core's conventions often correctly infer foreign keys, you can explicitly specify them using the [ForeignKey] attribute or by using the HasForeignKey method in Fluent API.

Using the [ForeignKey] attribute:


public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public int BlogId { get; set; } // Foreign key property

    // Explicitly map the BlogId foreign key to the Blog navigation property
    [ForeignKey("BlogId")]
    public Blog Blog { get; set; }
}
            

Navigation properties are crucial for working with relationships in EF Core. They allow you to traverse from one related entity to another.

When you query an entity that has navigation properties, EF Core can eagerly load related entities using:

Ensure your navigation properties are initialized (e.g., as empty collections) to avoid null reference exceptions when adding new related entities.

For complex relationship scenarios or when conventions aren't sufficient, consider using the Fluent API's HasOne, HasMany, and WithOne / WithMany methods in your DbContext.OnModelCreating method.