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.
The one-to-many relationship can be configured in the following ways.
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.
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.
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.
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.
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.
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.
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>
)
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.
modelBuilder.Entity<student>()
starts with the Student
entity.
.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.
Grade
entity.
.WithMany(g => g.Students)
specifies that the Grade
entity class includes many Student
entities. Here, many infers the ICollection
type property.
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.
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);
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();