设计器 TPH 继承

本分步演练介绍如何使用实体框架设计器(EF 设计器)在概念模型中实现每个层次结构一张表 (TPH) 继承。 TPH 继承使用一个数据库表维护继承层次结构中的所有实体类型的数据。

在本演练中,我们将 Person 表映射到三个实体类型:Person(基类型)、Student(派生自 Person)和 Instructor(派生自 Person)。 我们将从数据库创建一个概念模型 (Database First),然后更改该模型,以使用 EF 设计器实现 TPH 继承。

可以使用 Model First 映射到 TPH 继承,但你必须编写自己的数据库生成工作流,此工作流很复杂。 然后,将此工作流分配给 EF 设计器中的“数据库生成工作流”属性。 一种更简单的替代方法是使用 Code First。

其他继承选项

每个类型一张表 (TPT) 是另一种继承类型,其中,数据库中不同的表映射到参与继承的实体。  有关如何使用 EF 设计器映射每个类型一张表继承的信息,请参阅 EF 设计器 TPT 继承

实体框架运行时支持每个具体类型一张表继承 (TPC) 模型和混合继承模型,但 EF 设计器不支持。 如果要使用 TPC 或混合继承,你有两个选择:使用 Code First,或手动编辑 EDMX 文件。 如果选择使用 EDMX 文件,映射详细信息窗口将进入“安全模式”,你将无法使用设计器更改映射。

先决条件

若要完成此演练,您需要:

设置项目

  • 打开 Visual Studio 2012。
  • 选择“文件”->“新建”->“项目”
  • 在左窗格中,单击“Visual C#”,然后选择“控制台”模板
  • 输入 TPHDBFirstSample 作为名称
  • 选择“确定”

创建模型

  • 在解决方案资源管理器中右键单击项目名称,然后选择“添加”->“新建项”
  • 从左侧菜单中选择“数据”,然后在“模板”窗格中选择“ADO.NET 实体数据模型”
  • 输入 TPHModel.edmx 作为文件名,然后单击“添加”
  • 在“选择模型内容”对话框中,选择“从数据库生成”,然后单击“下一步”
  • 单击“新建连接”。 在“连接属性”对话框中,输入服务器名称(例如 (localdb)\mssqllocaldb),选择身份验证方法,键入 School 作为数据库名称,然后单击“确定”。 “选择数据连接”对话框将根据你的数据库连接设置进行更新。
  • 在“选择数据库对象”对话框中,在“表”节点下,选择“Person”表
  • 单击“完成” 。

将显示 Entity Designer,它提供用于编辑模型的设计图面。 在“选择数据库对象”对话框中选择的所有对象都已经添加到模型中。

这就是 Person 表在数据库中的外观。

Person Table 

实现每个层次结构一张表继承

Person 表包含 Discriminator 列,该列可以具有以下两个值之一:“Student”和“Instructor”。 Person 表将根据该值映射到 Student 实体或 Instructor 实体。 Person 表还有另外两列,即 HireDate 和 EnrollmentDate,它们必须可为 null,因为一个人不能同时既是学生又是教师(至少在本演练中不能)。

添加新实体

  • 添加一个新实体。 为此,请右键单击实体框架设计器设计图面的空白区域,然后选择“添加”->“实体”
  • 键入“Instructor”作为“实体名称”,然后从“基类型”下拉列表中选择“Person”
  • 单击“确定”。
  • 再添加一个新实体。 键入“Student”作为“实体名称”,然后从“基类型”下拉列表中选择“Person”

已向设计图面添加两个新的实体类型。 箭头从新的实体类型指向 Person 实体类型;这表示 Person 是新实体类型的基类型。

  • 右键单击“Person”实体的“HireDate”属性。 选择“剪切”(或使用 Ctrl-X 键)。
  • 右键单击“Instructor”实体并选择“粘贴”(或使用 Ctrl-V 键)。
  • 右键单击“HireDate”属性,然后选择“属性”
  • 在“属性”窗口中,将“可为 Null”属性设置为 false
  • 右键单击“Person”实体的“EnrollmentDate”属性。 选择“剪切”(或使用 Ctrl-X 键)。
  • 右键单击“Student”实体并选择“粘贴”(或使用 Ctrl-V 键)。
  • 选择“EnrollmentDate”属性,并将“可为 Null”属性设置为 false
  • 选择“Person”实体类型。 在“属性”窗口中,将其“抽象”属性设置为 true
  • 从 Person 中删除 Discriminator 属性。 下一部分将解释删除它的原因。

映射实体

  • 右键单击“Instructor”并选择“表映射”。Instructor 实体在“映射详细信息”窗口中处于选中状态。

  • 在“映射详细信息”窗口中单击“<添加表或视图>”。 “<添加表或视图>”字段变成下拉列表,其中包含选定实体可映射到的表或视图

  • 从下拉列表中选择“Person”

  • “映射详细信息”窗口将根据默认列映射以及一个关于添加条件的选项进行更新。

  • 单击“<添加条件>”。 “<添加条件>”字段变成一个包含列的下拉列表(可为其中各列设置条件)。

  • 从下拉列表中选择“Discriminator”

  • 在“映射详细信息”窗口的“运算符”列中,从下拉列表中选择 =

  • 在“值/属性”列中,键入 Instructor。 最终结果应如下所示:

    Mapping Details

  • 对 Student 实体类型重复这些步骤,但要使条件等于 Student 值。
    我们之所以要删除 Discriminator 属性,是因为你不能多次映射一个表列。 此列将用于条件映射,因此它不能同时用于属性映射。 仅当条件使用“为 Null”或“不为 Null”比较时,它才能同时用于这两者。

“每个层次结构一个表”继承实现完毕。

Final TPH

使用模型

打开 Program.cs 文件,其中定义了 Main 方法。 将以下代码粘贴到 Main 函数中。 该代码执行三个查询。 第一个查询返回所有 Person 对象。 第二个查询使用 OfType 方法返回 Instructor 对象。 第三个查询使用 OfType 方法返回 Student 对象。

    using (var context = new SchoolEntities())
    {
        Console.WriteLine("All people:");
        foreach (var person in context.People)
        {
            Console.WriteLine("    {0} {1}", person.FirstName, person.LastName);
        }

        Console.WriteLine("Instructors only: ");
        foreach (var person in context.People.OfType<Instructor>())
        {
            Console.WriteLine("    {0} {1}", person.FirstName, person.LastName);
        }

        Console.WriteLine("Students only: ");
        foreach (var person in context.People.OfType<Student>())
        {
            Console.WriteLine("    {0} {1}", person.FirstName, person.LastName);
        }
    }