演练5-7:Contoso大学校园管理系统(实现继承) 一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承 二、创建 Person 类 三、在模型中增加 Person 实体类型 四、修改 InstructorID 和 StudentID 为 PersonID 七、测试

***操作视频下载:     ***

      在上一次教程中,你已经能够处理并发异常。这个教程将会展示如何在数据模型中实现继承。

      在面向对象的程序设计中,你可以通过继承来清除冗余的代码。在这个教程中,你将要通过修改教师 Instructor 和学生 Student 类,以便使他们从包含类似 LastName 属性的 Person 类中派生。对于 Web 页面不需要任何改动,你需要修改一点代码,这些修改将会被自动反射到数据库中。

      在面向对象的程序设计中,你可以通过对相关的类使用继承来使得工作更加简单。例如,教师 Instructor 和学生 Student 类在学校 School 数据模型*享多个属性,带来了冗余的代码。

演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试

      假设你希望清除在教师 Instructor 和学生 Student 之间所共享的属性带来的冗余代码。可以创建一个 Person 基类,其中仅仅包含他们共享的属性,然后,使得教师 Instructor 和学生 Student 类从 Person 基类派生,如下图所示。

 演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试

      在数据库中这种继承结构可以有多种表现形式,可以创建一个名为 Person 的表,在这个独立的表中包含教师和学生所有的信息。既包括他们独自拥有的属性 ( 例如教师的 HireDate ,以及学生的 EnrollmentDate ),也包括它们共有的属性 ( 例如 LastName, FirstName )。通常你还需要一个用于识别当前类型的列 discriminator 来标识当前行的类型。( 在这里,标识列的内容为 Instructor 来表示教师,Student 来表示学生 )

演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试

      使用单个数据库表来生成实体继承结构的模式称为单表继承模式 TPH (table-per-hierarchy )。

      另外一种方式是使得数据库看起来类似继承结构。例如,在 Person 表中仅仅包含他们共有的名字属性,而将不同的时间分别保存到独立的 Instructor 和 Student 表中。 演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试

      这种每种实体类对应一张数据库表的模式称为类型表 TPT  继承 (table per type )。 在 EF 中,TPH 继承比 TPT 继承有更好的性能,因为 TPT 继承需要复杂的连接查询。这个教程演示如何实现 TPH 继承。你需要完成如下的步骤:

  • 创建 Person 类,将 Instructor 和 Student 类从 Person 类中派生
  • 在数据库上下文类中增加模型到数据库的映射代码
  • 将项目中的 InstructorID 和 StudentID 修改为使用 PersonID.

二、创建 Person 类

      在 Model 文件夹中,创建 Person.cs ,使用下面的代码替换原有的代码。

演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
    public abstract class Person
    {
        [Key]
        public int PersonID { get; set; }

        [Required(ErrorMessage = "Last name is required.")]
        [Display(Name="Last Name")]
        [MaxLength(50)]
        public string LastName { get; set; }

        [Required(ErrorMessage = "First name is required.")]
        [Column("FirstName")]
        [Display(Name = "First Name")]
        [MaxLength(50)]
        public string FirstMidName { get; set; }

        public string FullName 
        {
            get
            {
                return LastName + ", " + FirstMidName;
            }
        }
    }
}
演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试

      在 Instructor.cs 文件中,将 Instructor 从 Person 派生出来,删除 key 和 name 字段。代码如下所示。

演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
    public class Instructor : Person
    {
        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
        [Required(ErrorMessage = "Hire date is required.")]
        [Display(Name = "Hire Date")]
        public DateTime? HireDate { get; set; }

        public virtual ICollection<Course> Courses { get; set; }
      
        public virtual OfficeAssignment OfficeAssignment { get; set; }
    }
}
演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试

对 Student.cs 文件进行类似的修改,修改后的 Student 类如下所示。

演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
    public class Student : Person
    {
        [Required(ErrorMessage = "Enrollment date is required.")]
        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
        [Display(Name = "Enrollment Date")]
        public DateTime? EnrollmentDate { get; set; }

        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
}
演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试

三、在模型中增加 Person 实体类型

      在 SchoolContext.cs 文件中,对 Person 实体类型增加一个 DbSet 类型的属性。

public DbSet<Person> People { get; set; }

这就是在 EF 中配置 TPH 继承所有需要完成的工作。如你所见,当数据库重建的时候,EF 会自动创建 Person 表。

四、修改 InstructorID 和 StudentID 为 PersonID

       进行一次全局的替换 ( 项目中所有的文件 ),将 InstructorID 修改为 PersonID,StudentID 修改为 PersonID。注意区分大小写。( 这里演示了使用类名加上 ID 作为主键的一个缺点。如果你没有使用类名加上 ID 作为主键,这里就不需要重新命名 )

       进行模型迁移,add-migration inheritance, update-database,遇到问题的话,直接删了数据库,且更改webconfig,然后再来。

七、测试

      运行程序,在各个页面上检查一下,所有的工作如以前一样。 在解决方案管理器上,双击 School.sdf 数据库,在服务器资源管理器中打开,展开 School.sdf,然后选择 Tables,你会看到 Student 和 Instructor 表已经被 Person 表替换掉了。展开 Person 表,你会看到其中拥有原来 Student 和 Instructor 中所有的列,加上 discriminator 列。

演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试

      下图展示了新的 School 数据库的结构。

演练5-7:Contoso大学校园管理系统(实现继承)
一、单表继承 (Table-per-Hierarchy)  vs 类型表 (Table-per-Type) 继承
二、创建 Person 类
三、在模型中增加 Person 实体类型
四、修改 InstructorID 和 StudentID 为 PersonID
七、测试

      对于 Person ,Student 和 Instructor 类,通过 TPH 实现了继承。对于更多的关于其他继承结构,可以查看 Morteza Manavi 的博客 Inheritance Mapping Strategies。在下一次的教程中,将会学习实现仓储和单元模式的一些途径。