Configure One-to-Many Relationship in EF 6

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

Let's configure 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 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 Relationship:

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

Let's see an example of all the conventions which create in one-to-many relationship.

Convention 1:

We want to establish one-to-many relationship between Student and Grade entities where many students are associated with one Grade. It means each Student entity points to one Grade. This can be achieved by included 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 reference navigation property of Grade class. So, there can be many Students for a single Grade. This will result in one-to-many relationship between 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 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 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 collection navigation property of type ICollection<Student>. This also results in one-to-many relationship between Student and Grade entities. This example produces the same result in the database as convention 1.

Convention 3:

Including navigation property at both ends will also result in 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 reference navigation property of the Grade type and the Grade entity class includes collection navigation property of the ICollection<Student> type which results in one-to-many relationship. This example produces the same result in the database as convention 1.

Convention 4:

Fully defined relationship at both ends will creates 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 one-to-many relationship with 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 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 will create nullable GradeId column in the database because we have used Nullable<int> type (? is a shortcut for Nullable<int>)

Configure One-to-Many Relationship using Fluent API

Mostly, you don't need to configure the one-to-many relationship in entity framework because one-to-many relationship conventions covers all combination. However, you may configure a relationship using Fluent API for make it 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 one-to-many relationship for the above entities using Fluent API by overriding 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);          }
    }
}

The above example will create following tables in the database.

one-to-one relationship in code first

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 Student entity.

Then, .HasRequired<grade>(s => s.CurrentGrade) specifies that the Student entity has required CurrentGrade property. This will create NotNull foreign key column in the DB.

Now, it's time to configure the other end of the relationship - Grade entity.

.WithMany(g => g.Students) specifies that the Grade entity class includes many Student entities. Here, Many infers 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 foreign key using HasForeignKey method.

.HasForeignKey<int>(s => s.CurrentGradeId); specifies the foreign key property in the Student entity.

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


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

Configure NotNull ForeignKey using Fluent API:

In the convention 1, we have seen that it creates optional one-to-many relationship which creates nullable foreign key column in the database. To make it NotNull column, configure it using Fluent API as shown below.


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

The HasRequired<t>() method makes a NotNull column in the database.

Configure Cascade Delete using Fluent API:

Cascade delete means automatically deletes child row when related parent row is deleted. For example, if Grade is deleted then all the students in that Grade should also be deleted automatically.


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