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:
- Principal entity has a foreign key property: This is the most common and recommended approach. The principal entity has a foreign key property referencing the dependent entity, and the dependent entity has a single navigation property.
- Dependent entity has a foreign key property: Similar to the above, but the roles are reversed.
- Shared primary key: Both entities share the same primary key. This is often used for inheritance or when one entity is a strict subset of another.
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
Navigation properties are crucial for working with relationships in EF Core. They allow you to traverse from one related entity to another.
- Collection navigation properties (e.g.,
List<T>
,ICollection<T>
) represent the "many" side of a relationship. - Reference navigation properties (e.g.,
T
) represent the "one" side of a relationship.
When you query an entity that has navigation properties, EF Core can eagerly load related entities using:
Include()
: For loading specific related entities.ThenInclude()
: For chaining includes on already included navigation properties.- Lazy Loading: Requires virtual navigation properties and the
Microsoft.EntityFrameworkCore.Proxies
package. - Explicit Loading: Manually loading related entities when needed.
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.