EF 6 Code-First系列文章目錄:
- 1 翻譯系列:什么是Code First(EF 6 Code First 系列)
- 2.翻譯系列:為EF Code-First設置開發環境(EF 6 Code-First系列)
- 3.翻譯系列:EF Code-First 示例(EF 6 Code-First系列)
- 4.翻譯系列:EF 6 Code-First默認約定(EF 6 Code-First系列)
- 5.翻譯系列:EF 6中數據庫的初始化(EF 6 Code-First 系列)
- 6.翻譯系列:EF 6 Code-First中數據庫初始化策略(EF 6 Code-First系列
- 7.翻譯系列:EF 6中的繼承策略(EF 6 Code-First 系列)
- 8.翻譯系列: EF 6中配置領域類(EF 6 Code-First 系列)
- 9.翻譯系列:EF 6以及EF Core中的數據注解特性(EF 6 Code-First系列)
- 9.1 翻譯系列:數據注解特性之----Table【EF 6 Code-First 系列】
- 9.2 翻譯系列:數據注解特性之---Column【EF 6 Code First系列】
- 9.3 翻譯系列:數據注解特性之Key【EF 6 Code-First 系列】
- 9.4 翻譯系列:EF 6以及 EF Core中的NotMapped特性(EF 6 Code-First系列)
- 9.5 翻譯系列:數據注解之ForeignKey特性【EF 6 Code-First系列】
- 9.6 翻譯系列:數據注解之Index特性【EF 6 Code-First系列】
- 9.7 翻譯系列:EF數據注解特性之--InverseProperty【EF 6 Code-First系列】
- 9.8 翻譯系列:數據注解特性之--Required 【EF 6 Code-First系列】
- 9.9 翻譯系列:數據注解特性之--MaxLength 【EF 6 Code-First系列】
- 9.10 翻譯系列:EF數據注解特性之StringLength【EF 6 Code-First系列】
- 9.11 翻譯系列:數據注解特性之--Timestamp【EF 6 Code-First系列】
- 9.12 翻譯系列:數據注解特性之ConcurrencyCheck【EF 6 Code-First系列】
- 10.翻譯系列:EF 6中的Fluent API配置【EF 6 Code-First系列】
- 10.1.翻譯系列:EF 6中的實體映射【EF 6 Code-First系列】
- 10.2.翻譯系列:使用Fluent API進行屬性映射【EF 6 Code-First】
- 11.翻譯系列:在EF 6中配置一對零或者一對一的關系【EF 6 Code-First系列】
- 12.翻譯系列:EF 6 中配置一對多的關系【EF 6 Code-First系列】
- 13.翻譯系列:Code-First方式配置多對多關系【EF 6 Code-First系列】
- 14.翻譯系列:從已經存在的數據庫中生成上下文類和實體類【EF 6 Code-First系列】
- 15.翻譯系列:EF 6中的級聯刪除【EF 6 Code-First 系列】
- 16.翻譯系列:EF 6 Code -First中使用存儲過程【EF 6 Code-First系列】
- 17.翻譯系列:將Fluent API的配置遷移到單獨的類中【EF 6 Code-First系列】
- 18.翻譯系列:EF 6 Code-First 中的Seed Data(種子數據或原始測試數據)【EF 6 Code-First系列】
- 19.翻譯系列:EF 6中定義自定義的約定【EF 6 Code-First約定】
- 20.翻譯系列:Code-First中的數據庫遷移技術【EF 6 Code-First系列】
- 20.1翻譯系列:EF 6中自動數據遷移技術【EF 6 Code-First系列】
- 20.2.翻譯系列:EF 6中基於代碼的數據庫遷移技術【EF 6 Code-First系列】
- 21.翻譯系列:Entity Framework 6 Power Tools【EF 6 Code-First系列】
這里我們將學習如何在兩個實體(領域類)之間配置一對多的關系。
我們使用Student和Grade兩個實體配置一對多的關系,一個Grade中可以有很多的Students。
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; }
}
在上面兩個實體實現一對多關系之后,數據庫中就會生成下面兩個表:
一對多的關系,可以通過下面的方式進行配置:
- 通過默認的約定
- 使用Fluent API進行配置
通過默認約定配置一對多的關系
在EF中有某些約定,只要實體遵循這些約定,EF就會為我們在數據庫自動生成一對多的關系數據表。你不用進行任何其他的配置。
我們來看看一對多關系的所有的約定情況吧:
約定1:
我們想要在Student實體和Grade實體之間建立一對多的關系,並且很多學生關聯到一個Grade。這就意味着,每一個Student實體指向一個Grade。這種情況可以通過在Student類中包含一個Grade類型的導航屬性做到,例如:
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; }
}
在上面的例子中,Student類包含了一個Grade類型的導航屬性,這樣就會在Students和Grades表之間生成一對多關系,並且生成外鍵Grade_GradeId:
請注意:因為應用類型的屬性是可空的,所以在Students表中創建的外鍵列Grade_GradeId是可空的。你可以使用Fluent API配置不為空的外鍵列。
約定2:
另外一個約定就是,在主體實體中包含一個集合類型的導航屬性,例如:
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; }
}
在上面例子中Grade實體中包含一個集合類型ICollection的導航屬性Students.所以這種也會在Students和Grades表之間生成一對多的關系。生成的數據庫結構和約定1中一樣。
約定3:
在兩個實體中,都包含導航屬性:
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; }
}
在上面的代碼中,Student實體包含一個Grade類型的導航屬性,同樣,Grade實體包含一個集合類型ICollection的屬性.結果也是在兩個表之間生成一對多的關系。生成的數據庫結果和約定1,約定2中一樣。
約定4:
在兩個實體中,完整的定義關系,也會根據約定生成一對多的關系表:
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; }
}
在上面的例子中,Student實體中,包含一個外鍵屬性GradeId,還有一個Grade類型的導航屬性。這樣就會生成一對多的關系表。並且Student表中生成的外鍵GradeId是不可空的:

如果GradeId是可空的int類型,那么就會生成可空的外鍵:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int? GradeId { get; set; }
public Grade Grade { get; set; }
}
上面的代碼將會生成一個可空的外鍵GradeId,?只是類型Nullable的簡寫。
使用Fluent API配置一對多的關系
通常情況下,在EF中你不用配置一對多的關系,因為默認的約定,已經幫我們配置好了。然而你可以使用Fluent API來配置一對多的關系,比默認生成的表更好維護一點。
看看下面的Student和Grade實體:
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; }
}
你可以使用Fluent API,重寫上下文類中的OnModelCreating方法,來給上面的代碼配置一對多的關系:
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);
}
}
我們來一步步理解上面的代碼:
- 首先從一個實體類開始配置,所以modelBuilder.Entity()就是從Student實體開始配置。
- 然后,.HasRequired(s => s.CurrentGrade)指定Student實體,必須要有CurrentGrade屬性,這就會在數據庫中生成一個不為空的外鍵。
- 現在,現在就是配置關系的另外一邊,也就是Grade實體。
- .WithMany(g => g.Students)指定Grade實體包含很多Student實體,這里可以通過ICollcetion類型的屬性推斷出來。
- 然后,如果Student實體,不遵循外鍵的約定【ID property convention】,我們就可以通過HasForeignKey方法指定外鍵的名稱。
- HasForeignKey(s => s.CurrentGradeId),指定了Student實體中的外鍵屬性。
還有另外一種方式配置一對多關系,就是從Grade實體開始配置,而不是Student實體。下面的代碼,生成的數據庫和上面的代碼生成的是一樣的:
modelBuilder.Entity<Grade>()
.HasMany<Student>(g => g.Students)
.WithRequired(s => s.CurrentGrade)
.HasForeignKey<int>(s => s.CurrentGradeId);
代碼生成的數據庫結構如下:
使用Fluent API配置不為空的外鍵列
在約定1中,我們看到生成的一對多關系中,外鍵是可空的,為了生成不為空的外鍵列,我們可以這樣,使用HasRequired方法:
modelBuilder.Entity<Student>()
.HasRequired<Grade>(s => s.CurrentGrade)
.WithMany(g => g.Students);
使用Fluent API配置級聯刪除
級聯刪除意味着:當父行被刪除之后,自動刪除相關的子行。例如:如果Grade被刪除了,那么所有在這個Grade中的Students應該同樣被自動刪除。下面的代碼,使用WillCascadeOnDelete方法配置級聯刪除:
modelBuilder.Entity<Grade>()
.HasMany<Student>(g => g.Students)
.WithRequired(s => s.CurrentGrade)
.WillCascadeOnDelete();
好了,一對多的關系就介紹到這里,下面一節講解多對多關系。