使用 Entity Framework 4.0 Database First 和 ASP.NET 4 Web Forms 入门 - 第 6 部分

作者 :Tom Dykstra

Contoso University 示例 Web 应用程序演示如何使用 Entity Framework 4.0 和 Visual Studio 2010 创建 ASP.NET Web Forms应用程序。 有关教程系列的信息,请参阅 系列中的第一个教程

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

在上一教程中,你通过添加和删除关系以及添加与现有实体有关系的新实体来处理相关数据。 本教程将演示如何在数据模型中实现继承。

在面向对象的编程中,可以使用继承来更轻松地使用相关类。 例如,可以创建Instructor派生自基类的 PersonStudent 类。 可以在实体框架中的实体之间创建相同类型的继承结构。

在本教程的这一部分中,不会创建任何新网页。 而是将派生实体添加到数据模型,并修改现有页面以使用新实体。

每个层次结构的表与每个类型表的继承

数据库可以将有关相关对象的信息存储在一个表或多个表中。 例如,在 School 数据库中, Person 表在单个表中包含有关学生和讲师的信息。 其中一些列仅适用于 () HireDate 的讲师,有些仅适用于 (EnrollmentDate) 的学生,有些则适用于 (LastNameFirstName) 。

image11

可以将实体框架配置为创建Instructor继承自实体的 PersonStudent 实体。 这种从单一数据库表生成实体继承结构的模式称为每个 层次结构表 (TPH) 继承。

对于课程, School 数据库使用不同的模式。 在线课程和现场课程存储在单独的表中,每个表都有指向该表的 Course 外键。 这两种课程类型通用的信息仅 Course 存储在表中。

image12

可以配置实体框架数据模型, OnlineCourse 以便 和 OnsiteCourse 实体从实体 Course 继承。 这种从每个类型的单独表中生成实体继承结构的模式,每个单独的表引用存储所有类型通用数据的表, (TPT) 继承。

TPH 继承模式通常比 TPT 继承模式在实体框架中提供的性能更好,因为 TPT 模式可能会导致复杂的联接查询。 本演练演示如何实现 TPH 继承。 你将通过执行以下步骤来执行此操作:

  • 创建Instructor派生自 Person的 和 Student 实体类型。
  • 将与派生实体相关的属性从 Person 实体移动到派生实体。
  • 对派生类型中的属性设置约束。
  • 使实体 Person 成为抽象实体。
  • 使用指定如何确定行是否Person表示派生类型的条件将每个派生实体映射到Person表。

添加讲师和学生实体

打开 SchoolModel.edmx 文件,右键单击设计器中的未占用区域,选择“ 添加”,然后选择“ 实体”。

image01

在“ 添加实体 ”对话框中,为实体 Instructor 命名,并将其 “基类型” 选项设置为 Person

image02

单击" 确定"。 设计器创建一个 Instructor 派生自实体的 Person 实体。 新实体尚不具有任何属性。

image03

重复该过程以创建 Student 同样派生自 Person的实体。

只有讲师有雇用日期,因此你需要将此属性从 Person 实体移动到 Instructor 实体。 在 实体中 Person ,右键单击 属性, HireDate 然后单击“ 剪切”。 然后右键单击实体中的Instructor“属性”,然后单击“粘贴”。

image04

实体的 Instructor 雇用日期不能为 null。 右键单击 HireDate 属性,单击“ 属性”,然后在 “属性” 窗口中将 更改为 NullableFalse

image05

重复此过程,将 EnrollmentDate 属性从 Person 实体移动到 Student 实体。 确保还针对 EnrollmentDate 属性将 设置为 。FalseNullable

现在,实体 Person 仅具有通用 Instructor 的属性,并且 Student 除了导航属性之外 (实体(你不会) 移动这些属性),该实体只能用作继承结构中的基实体。 因此,需要确保它永远不会被视为独立实体。 右键单击实体 Person ,选择“ 属性”,然后在 “属性” 窗口中将 Abstract 属性的值更改为 True

image06

将讲师和学生实体映射到人员表

现在,需要告诉实体框架如何区分 Instructor 数据库中的 和 Student 实体。

右键单击实体, Instructor 然后选择“ 表映射”。 在 “映射详细信息 ”窗口中,单击“ 添加表”或“视图 ”,然后选择“ 人员”。

image07

单击“ 添加条件”,然后选择“ HireDate”。

image09

“运算符 ”更改为 “Is ”,将 “值/属性” 更改为 “非 Null”。

image10

Students实体重复该过程,指定在列不为 null 时EnrollmentDate此实体映射到Person表。 然后保存并关闭数据模型。

生成项目,以便将新实体创建为类,并使它们在设计器中可用。

使用讲师和学生实体

创建处理学生和讲师数据的网页时,将数据绑定到Person实体集,并在 或 EnrollmentDate 属性上HireDate进行筛选,以将返回的数据限制为学生或讲师。 但是,现在,将每个数据源控件绑定到 Person 实体集时,可以指定应仅 Student 选择 或 Instructor 实体类型。 由于实体框架知道如何区分实体集中的学生和讲师 Person ,因此可以 Where 删除手动输入的属性设置来执行此操作。

在 Visual Studio Designer中,可以指定控件应在向导的 EntityTypeFilter 下拉框中选择的Configure Data Source实体类型EntityDataSource,如以下示例所示。

image13

“属性” 窗口中,可以删除 Where 不再需要的子句值,如以下示例所示。

image14

但是,由于已更改控件的EntityDataSource标记以使用 ContextTypeName 属性,因此无法在已创建的控件上运行EntityDataSource“配置数据源”向导。 因此,你将改为通过更改标记进行所需的更改。

打开 Students.aspx 页。 在 控件中 StudentsEntityDataSource ,删除 Where 属性并添加 属性 EntityTypeFilter="Student" 。 标记现在将类似于以下示例:

<asp:EntityDataSource ID="StudentsEntityDataSource" runat="server" 
        ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
        EntitySetName="People" EntityTypeFilter="Student"
        Include="StudentGrades"
        EnableDelete="True" EnableUpdate="True" 
        OrderBy="it.LastName" >
    </asp:EntityDataSource>

EntityTypeFilter设置 属性可确保EntityDataSource控件仅选择指定的实体类型。 如果要同时检索 StudentInstructor 实体类型,则不会设置此属性。 (仅当使用该控件进行只读数据访问时,才可选择使用一个 EntityDataSource 控件检索多个实体类型。如果使用控件 EntityDataSource 插入、更新或删除实体,并且它绑定到的实体集可以包含多个类型,则只能使用一个实体类型,并且必须设置此属性。)

SearchEntityDataSource控件重复该过程,但只删除选择Student实体的属性部分Where,而不是完全删除属性。 控件的开始标记现在将类似于以下示例:

<asp:EntityDataSource ID="SearchEntityDataSource" runat="server" 
        ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
        EntitySetName="People" EntityTypeFilter="Student"
        Where="it.FirstMidName Like '%' + @StudentName + '%' or it.LastName Like '%' + @StudentName + '%'" >

运行页面,验证它是否仍然像以前一样工作。

image15

更新在前面的教程中创建的以下页面,以便它们使用新的 StudentInstructor 实体而不是 Person 实体,然后运行它们以验证它们是否像以前一样工作:

  • StudentsAdd.aspx 中,将 添加到 EntityTypeFilter="Student"StudentsEntityDataSource 控件。 标记现在将类似于以下示例:

    <asp:EntityDataSource ID="StudentsEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
            EntitySetName="People" EntityTypeFilter="Student"
            EnableInsert="True" 
        </asp:EntityDataSource>
    

    image16

  • About.aspx 中,将 添加到 EntityTypeFilter="Student"StudentStatisticsEntityDataSource 控件并删除 Where="it.EnrollmentDate is not null"。 标记现在将类似于以下示例:

    <asp:EntityDataSource ID="StudentStatisticsEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
            EntitySetName="People" EntityTypeFilter="Student"
            Select="it.EnrollmentDate, Count(it.EnrollmentDate) AS NumberOfStudents"
            OrderBy="it.EnrollmentDate" GroupBy="it.EnrollmentDate" >
        </asp:EntityDataSource>
    

    image17

  • Instructors.aspxInstructorsCourses.aspx 中,将 添加到 EntityTypeFilter="Instructor"InstructorsEntityDataSource 控件并删除 Where="it.HireDate is not null"Instructors.aspx 中的标记现在类似于以下示例:

    <asp:EntityDataSource ID="InstructorsEntityDataSource" runat="server" 
                ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false"
                EntitySetName="People" EntityTypeFilter="Instructor" 
                Include="OfficeAssignment" 
                EnableUpdate="True">
            </asp:EntityDataSource>
    

    image18

    InstructorsCourses.aspx 中的标记现在类似于以下示例:

    <asp:EntityDataSource ID="InstructorsEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
            EntitySetName="People" EntityTypeFilter="Instructor" 
            Select="it.LastName + ',' + it.FirstMidName AS Name, it.PersonID">
        </asp:EntityDataSource>
    

    image19

由于这些更改,你已通过多种方式改进了 Contoso University 应用程序的可维护性。 你已将选择和验证逻辑移出 UI 层 (.aspx 标记) ,并使其成为数据访问层不可或缺的一部分。 这有助于将应用程序代码与将来可能对数据库架构或数据模型所做的更改隔离开来。 例如,你可以决定学生可能被聘为教师辅助人员,从而获得雇用日期。 然后,可以添加新属性,将学生与讲师区分开来,并更新数据模型。 Web 应用程序中的代码不需要更改,除非你想要显示学生的雇用日期。 添加 InstructorStudent 实体的另一个好处是,与代码引用 Person 实际是学生或讲师的对象相比,代码更易于理解。

现在,你已了解在实体框架中实现继承模式的一种方法。 在以下教程中,你将了解如何使用存储过程,以便更好地控制实体框架访问数据库的方式。