作者 :Tom Dykstra
Contoso University 示例 Web 应用程序演示如何使用 Entity Framework 4.0 和 Visual Studio 2010 创建 ASP.NET Web Forms应用程序。 有关教程系列的信息,请参阅 系列中的第一个教程
实现“每个层次结构一个表”继承
在上一教程中,你通过添加和删除关系以及添加与现有实体有关系的新实体来处理相关数据。 本教程将演示如何在数据模型中实现继承。
在面向对象的编程中,可以使用继承来更轻松地使用相关类。 例如,可以创建Instructor
派生自基类的 Person
和 Student
类。 可以在实体框架中的实体之间创建相同类型的继承结构。
在本教程的这一部分中,不会创建任何新网页。 而是将派生实体添加到数据模型,并修改现有页面以使用新实体。
每个层次结构的表与每个类型表的继承
数据库可以将有关相关对象的信息存储在一个表或多个表中。 例如,在 School
数据库中, Person
表在单个表中包含有关学生和讲师的信息。 其中一些列仅适用于 () HireDate
的讲师,有些仅适用于 (EnrollmentDate
) 的学生,有些则适用于 (LastName
、 FirstName
) 。
可以将实体框架配置为创建Instructor
继承自实体的 Person
和 Student
实体。 这种从单一数据库表生成实体继承结构的模式称为每个 层次结构表 (TPH) 继承。
对于课程, School
数据库使用不同的模式。 在线课程和现场课程存储在单独的表中,每个表都有指向该表的 Course
外键。 这两种课程类型通用的信息仅 Course
存储在表中。
可以配置实体框架数据模型, OnlineCourse
以便 和 OnsiteCourse
实体从实体 Course
继承。 这种从每个类型的单独表中生成实体继承结构的模式,每个单独的表引用存储所有类型通用数据的表, (TPT) 继承。
TPH 继承模式通常比 TPT 继承模式在实体框架中提供的性能更好,因为 TPT 模式可能会导致复杂的联接查询。 本演练演示如何实现 TPH 继承。 你将通过执行以下步骤来执行此操作:
- 创建
Instructor
派生自Person
的 和Student
实体类型。 - 将与派生实体相关的属性从
Person
实体移动到派生实体。 - 对派生类型中的属性设置约束。
- 使实体
Person
成为抽象实体。 - 使用指定如何确定行是否
Person
表示派生类型的条件将每个派生实体映射到Person
表。
添加讲师和学生实体
打开 SchoolModel.edmx 文件,右键单击设计器中的未占用区域,选择“ 添加”,然后选择“ 实体”。
在“ 添加实体 ”对话框中,为实体 Instructor
命名,并将其 “基类型” 选项设置为 Person
。
单击" 确定"。 设计器创建一个 Instructor
派生自实体的 Person
实体。 新实体尚不具有任何属性。
重复该过程以创建 Student
同样派生自 Person
的实体。
只有讲师有雇用日期,因此你需要将此属性从 Person
实体移动到 Instructor
实体。 在 实体中 Person
,右键单击 属性, HireDate
然后单击“ 剪切”。 然后右键单击实体中的Instructor
“属性”,然后单击“粘贴”。
实体的 Instructor
雇用日期不能为 null。 右键单击 HireDate
属性,单击“ 属性”,然后在 “属性” 窗口中将 更改为 Nullable
False
。
重复此过程,将 EnrollmentDate
属性从 Person
实体移动到 Student
实体。 确保还针对 EnrollmentDate
属性将 设置为 。False
Nullable
现在,实体 Person
仅具有通用 Instructor
的属性,并且 Student
除了导航属性之外 (实体(你不会) 移动这些属性),该实体只能用作继承结构中的基实体。 因此,需要确保它永远不会被视为独立实体。 右键单击实体 Person
,选择“ 属性”,然后在 “属性” 窗口中将 Abstract 属性的值更改为 True。
将讲师和学生实体映射到人员表
现在,需要告诉实体框架如何区分 Instructor
数据库中的 和 Student
实体。
右键单击实体, Instructor
然后选择“ 表映射”。 在 “映射详细信息 ”窗口中,单击“ 添加表”或“视图 ”,然后选择“ 人员”。
单击“ 添加条件”,然后选择“ HireDate”。
将 “运算符 ”更改为 “Is ”,将 “值/属性” 更改为 “非 Null”。
对Students
实体重复该过程,指定在列不为 null 时EnrollmentDate
此实体映射到Person
表。 然后保存并关闭数据模型。
生成项目,以便将新实体创建为类,并使它们在设计器中可用。
使用讲师和学生实体
创建处理学生和讲师数据的网页时,将数据绑定到Person
实体集,并在 或 EnrollmentDate
属性上HireDate
进行筛选,以将返回的数据限制为学生或讲师。 但是,现在,将每个数据源控件绑定到 Person
实体集时,可以指定应仅 Student
选择 或 Instructor
实体类型。 由于实体框架知道如何区分实体集中的学生和讲师 Person
,因此可以 Where
删除手动输入的属性设置来执行此操作。
在 Visual Studio Designer中,可以指定控件应在向导的 EntityTypeFilter 下拉框中选择的Configure Data Source
实体类型EntityDataSource
,如以下示例所示。
在 “属性” 窗口中,可以删除 Where
不再需要的子句值,如以下示例所示。
但是,由于已更改控件的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
控件仅选择指定的实体类型。 如果要同时检索 Student
和 Instructor
实体类型,则不会设置此属性。 (仅当使用该控件进行只读数据访问时,才可选择使用一个 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 + '%'" >
运行页面,验证它是否仍然像以前一样工作。
更新在前面的教程中创建的以下页面,以便它们使用新的 Student
和 Instructor
实体而不是 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>
在 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>
在 Instructors.aspx 和 InstructorsCourses.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>
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>
由于这些更改,你已通过多种方式改进了 Contoso University 应用程序的可维护性。 你已将选择和验证逻辑移出 UI 层 (.aspx 标记) ,并使其成为数据访问层不可或缺的一部分。 这有助于将应用程序代码与将来可能对数据库架构或数据模型所做的更改隔离开来。 例如,你可以决定学生可能被聘为教师辅助人员,从而获得雇用日期。 然后,可以添加新属性,将学生与讲师区分开来,并更新数据模型。 Web 应用程序中的代码不需要更改,除非你想要显示学生的雇用日期。 添加 Instructor
和 Student
实体的另一个好处是,与代码引用 Person
实际是学生或讲师的对象相比,代码更易于理解。
现在,你已了解在实体框架中实现继承模式的一种方法。 在以下教程中,你将了解如何使用存储过程,以便更好地控制实体框架访问数据库的方式。