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.
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.
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.
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 theStudent
entity. - Then,
.HasRequired<Grade>(s => s.CurrentGrade)
specifies that theStudent
entity has required theCurrentGrade
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 theGrade
entity class includes manyStudent
entities. Here, many infers theICollection
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 theHasForeignKey
method. .HasForeignKey<int>(s => s.CurrentGradeId);
specifies the foreign key property in theStudent
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.
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();
0 comments:
Post a Comment
Note: only a member of this blog may post a comment.