Configure One-to-Many Relationships in EF 6

Here, we will learn how to configure One-to-Many relationships between two entities (domain classes) in Entity Framework 6.x using the code-first approach.

Let's configure a one-to-many relationship between the following Student and Grade entities where there can be many students in one grade.


public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}
       
public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}

After implementing the one-to-many relationship in the above entities, the database tables for Student and Grade will look like below.

one-to-one relationship in code first

The one-to-many relationship can be configured in the following ways.

  1. By following Conventions
  2. By using Fluent API Configurations

Conventions for One-to-Many Relationships

There are certain conventions in Entity Framework which if followed in entity classes (domain classes) will automatically result in a one-to-many relationship between two tables in the database. You don't need to configure anything else.

Let's look at an example of all the conventions which create a one-to-many relationship.

Convention 1

We want to establish a one-to-many relationship between the Student and Grade entities where many students are associated with one Grade. It means that each Student entity points to a Grade. This can be achieved by including a reference navigation property of type Grade in the Student entity class, as shown below.


public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
}

In the above example, the Student class includes a reference navigation property of Grade class. So, there can be many students in a single grade. This will result in a one-to-many relationship between the Students and Grades table in the database, where the Students table includes foreign key Grade_GradeId as shown below.

one-to-one relationship in code first

Notice that the reference property is nullable, so it creates a nullable foreign key column Grade_GradeId in the Students table. You can configure NotNull foreign key using fluent API.

Convention 2

Another convention is to include a collection navigation property in the principal entity as shown below.


public class Student
{
    public int StudentId { get; set; }
    public string StudentName { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection<Student> Students { get; set; } 
}

In the above example, the Grade entity includes a collection navigation property of type ICollection<Student>. This also results in a one-to-many relationship between the Student and Grade entities. This example produces the same result in the database as convention 1.

Convention 3

Including navigation properties at both ends will also result in a one-to-many relationship, as shown below.


public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{
    public int GradeID { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }
    
    public ICollection<Student> Student { get; set; }
}

In the above example, the Student entity includes a reference navigation property of the Grade type and the Grade entity class includes a collection navigation property of the ICollection<Student> type which results in a one-to-many relationship. This example produces the same result in the database as convention 1.

Convention 4

A fully defined relationship at both ends will create a one-to-many relationship, as shown below.


public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    public int GradeId { get; set; }
    public Grade Grade { get; set; }
}

public class Grade
{

    public int GradeId { get; set; }
    public string GradeName { get; set; }
    
    public ICollection<Student> Student { get; set; }
}

In the above example, the Student entity includes foreign key property GradeId with its reference property Grade. This will create a one-to-many relationship with the NotNull foreign key column in the Students table, as shown below.

one-to-one relationship in code first

If the data type of GradeId is nullable integer, then it will create a null foreign key.


public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int? GradeId { get; set; }
    public Grade Grade { get; set; }
}

The above code snippet will create a nullable GradeId column in the database because we have used Nullable<int> type (? is a shortcut for Nullable<int>)

Configure a One-to-Many Relationship using Fluent API

Generally, you don't need to configure the one-to-many relationship in entity framework because one-to-many relationship conventions cover all combinations. However, you may configure relationships using Fluent API at one place to make it more maintainable.

Consider the following Student and Grade entity classes.


public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }

    public int CurrentGradeId { get; set; }
    public Grade CurrentGrade { get; set; }
}

public class Grade
{
    public int GradeId { get; set; }
    public string GradeName { get; set; }
    public string Section { get; set; }

    public ICollection<Student> Students { get; set; }
}

You can configure a one-to-many relationship for the above entities using Fluent API by overriding the OnModelCreating method in the context class, as shown below.


public class SchoolContext : DbContext
{
    public DbSet<Student> Students { get; set; }
    public DbSet<Grade> Grades { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // configures one-to-many relationship
        modelBuilder.Entity<Student>()
            .HasRequired<Grade>(s => s.CurrentGrade)
            .WithMany(g => g.Students)
            .HasForeignKey<int>(s => s.CurrentGradeId);          
    }
}

Let's understand the above code step by step.

  • First, we need to start configuring with any one entity class. So, modelBuilder.Entity<student>() starts with the Student entity.
  • Then, .HasRequired<Grade>(s => s.CurrentGrade) specifies that the Student entity has required the CurrentGrade property. This will create a NotNull foreign key column in the DB.
  • Now, it's time to configure the other end of the relationship - the Grade entity.
  • .WithMany(g => g.Students) specifies that the Grade entity class includes many Student entities. Here, many infers the ICollection type property.
  • Now, if the Student entity does not follow the Id property convention for foreign key, then we can specify the name of the foreign key using the HasForeignKey method.
  • .HasForeignKey<int>(s => s.CurrentGradeId); specifies the foreign key property in the Student entity.

Alternatively, you can start configuring the relationship with the Grade entity instead of the Student entity. The following code produces the same result as above.


modelBuilder.Entity<Grade>()
    .HasMany<Student>(g => g.Students)
    .WithRequired(s => s.CurrentGrade)
    .HasForeignKey<int>(s => s.CurrentGradeId);

The above example will create the following tables in the database.

one-to-one relationship in code first

Configure the NotNull ForeignKey using Fluent API

In convention 1, we have seen that it creates an optional one-to-many relationship which in turn creates a nullable foreign key column in the database. To make it a NotNull column, use the HasRequired() method as shown below.


modelBuilder.Entity<Student>()
    .HasRequired<Grade>(s => s.CurrentGrade)
    .WithMany(g => g.Students);

Configure Cascade Delete using Fluent API

Cascade delete means automatically deleting child rows when the related parent row is deleted. For example, if Grade is deleted then all the students in that Grade should also be deleted automatically. The following code configures the cascade delete using the WillCascadeOnDelete method.


modelBuilder.Entity<Grade>()
    .HasMany<Student>(g => g.Students)
    .WithRequired(s => s.CurrentGrade)
    .WillCascadeOnDelete();