12.翻译系列:EF 6 中配置一对多的关系【EF 6 Code-First系列】

原文链接:https://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx

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; }
}

在上面两个实体实现一对多关系之后,数据库中就会生成下面两个表:
12.翻译系列:EF 6 中配置一对多的关系【EF 6 Code-First系列】_第1张图片

一对多的关系,可以通过下面的方式进行配置:

  1. 通过默认的约定
  2. 使用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:
12.翻译系列:EF 6 中配置一对多的关系【EF 6 Code-First系列】_第2张图片

请注意:因为应用类型的属性是可空的,所以在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 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 { 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 { get; set; }
}

在上面的例子中,Student实体中,包含一个外键属性GradeId,还有一个Grade类型的导航属性。这样就会生成一对多的关系表。并且Student表中生成的外键GradeId是不可空的:

 

12.翻译系列:EF 6 中配置一对多的关系【EF 6 Code-First系列】_第3张图片
enter description here

 

如果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 Students { get; set; }
}

你可以使用Fluent API,重写上下文类中的OnModelCreating方法,来给上面的代码配置一对多的关系:

public class SchoolContext : DbContext
{
    public DbSet Students { get; set; }
    public DbSet Grades { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // configures one-to-many relationship
        modelBuilder.Entity()
            .HasRequired(s => s.CurrentGrade)
            .WithMany(g => g.Students)
            .HasForeignKey(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()
    .HasMany(g => g.Students)
    .WithRequired(s => s.CurrentGrade)
    .HasForeignKey(s => s.CurrentGradeId);

代码生成的数据库结构如下:
12.翻译系列:EF 6 中配置一对多的关系【EF 6 Code-First系列】_第4张图片

使用Fluent API配置不为空的外键列

在约定1中,我们看到生成的一对多关系中,外键是可空的,为了生成不为空的外键列,我们可以这样,使用HasRequired方法:

modelBuilder.Entity()
    .HasRequired(s => s.CurrentGrade)
    .WithMany(g => g.Students);
使用Fluent API配置级联删除

级联删除意味着:当父行被删除之后,自动删除相关的子行。例如:如果Grade被删除了,那么所有在这个Grade中的Students应该同样被自动删除。下面的代码,使用WillCascadeOnDelete方法配置级联删除:

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

好了,一对多的关系就介绍到这里,下面一节讲解多对多关系。

你可能感兴趣的:(12.翻译系列:EF 6 中配置一对多的关系【EF 6 Code-First系列】)