演练:映射继承 - 每个层次结构一个表(实体数据模型工具)
本主题介绍如何通过改变在 .edmx 文件中定义的概念模型来实现每个层次结构一个表继承。 每个层次结构一个表继承使用一个数据库表维护继承层次结构中的所有实体类型的数据。
在本演练中,您将通过修改 CourseManager 应用程序中使用的概念模型来实现每个层次结构一个表继承(有关更多信息,请参见本主题后面的“先决条件”一节)。
在 CourseManager 概念模型中,Person 实体类型具有两个属性,即 HireDate 和 EnrollmentDate,这两个属性可属于从 Person 继承的新实体类型(分别是 Instructor 和 Student)。 以下步骤概述了在本例中如何实现每个层次结构一个表继承。 本演练中的过程将提供更多详细信息。
创建两个新实体类型:Instructor 和 Student。
将每个新实体类型的基类型设置为 Person。
将 HireDate 属性从 Person 移到 Instructor,并将 EnrollmentDate 属性从 Person 移到 Student。
将 Person 实体类型设置为抽象类型。
将 Instructor 实体类型映射到具有以下两个条件的 Person 表:HireDate Is Not Null 和 EnrollmentDate Is Null。
将 Student 实体类型映射到具有以下两个条件的 Person 表:EnrollmentDate Is Not Null 和 HireDate Is Null。
必备条件
若要完成本演练,必须生成 CourseManager 应用程序。 有关更多信息和说明,请参见实体框架快速入门。 生成此应用程序之后,将通过修改其概念模型和映射来实现每个层次结构一个表继承。 然后,您将扩展此应用程序的功能,以显示选定课程的注册情况。
注意: |
---|
因为本文档中的许多演练主题都使用该 CourseManager 应用程序作为起点,所以建议在本演练中使用 CourseManager 应用程序的副本,而不要编辑原始 CourseManager 代码。 |
本演练假定读者具备 Visual Studio、.NET Framework 的基本知识,并能使用 Visual C# 或 Visual Basic 进行编程。
实现每个层次结构一个表继承
在此过程中,您将改变 SchoolModel 来实现每个层次结构一个表继承。 若要观看下面过程的视频演示,请参见如何(可能为英文网页)。
实现每个层次结构一个表继承
在 Visual Studio 中打开 CourseManager 解决方案。
在解决方案资源管理器中,双击 School.edmx 文件。
该 School.edmx 文件将在 ADO.NET 实体数据模型设计器(实体设计器)中打开。
右键单击实体设计器设计图面的空白区域,指向**“添加”,然后单击“实体”**。
此时将显示**“新建实体”**对话框。
对于**“实体名称”请键入 Instructor,然后从“基类型”**下拉列表中选择 Person。
单击**“确定”**。
新的实体类型创建完毕,并且显示在设计图面上。
重复步骤 3 到 5,但是在第 2 步中,对于**“实体名称”**请键入 Student。
现在,在设计图面上显示有两个新的实体类型:Instructor 和 Student。 箭头从新的实体类型指向 Person 实体类型;这表示 Person 是新实体类型的基类型。
右键单击 Person 实体类型的 HireDate 属性(在**“标量属性”下面)。 选择“剪切”**。
右键单击 Instructor 实体类型的**“标量属性”,然后选择“粘帖”**。
右键单击 HireDate 属性,然后选择**“属性”**。
在**“属性”窗口中,将“可以为 Null”**属性设置为 false。
重复步骤 7 到 10,但须剪切 Person 实体类型的 EnrollmentDate 属性,并将之粘帖到 Student 实体类型中。
选择 Person 实体类型。 在**“属性”窗口中,将其“抽象”**属性设置为 true。
通常,不必使用抽象类型为“每个层次结构一个表”方案建模。 在本示例中使用抽象类型来演示其在概念模型中的用法。
注意: 本过程中的其余步骤要求使用“映射详细信息”窗口。如果没有显示此窗口,请右键单击设计图面,然后选择“映射详细信息”。 选择 Instructor 实体类型,然后单击**“映射详细信息”窗口中的“<添加表或视图>”**。
**“<添加表或视图>”**字段变成下拉列表,其中包含选定实体可映射到的表或视图。
从下拉列表中选择 Person。
**“映射详细信息”**窗口将会根据默认列映射以及一个关于添加条件的选项而进行更新。
单击**“<添加条件>”**。
**“<添加条件>”**字段变成一个包含列的下拉列表(可为其中各列设置条件)。
从下拉列表中选择 HireDate。
出现另一个**“<添加条件>”**字段。
在**“映射详细信息”窗口的“运算符”列中,从下拉列表中选择“Is”**。
在**“属性/值”列中,选择“Not Null”**。
单击**“<添加条件>”**。
从下拉列表中选择 EnrollmentDate。
在**“运算符”列中,从下拉列表中选择“Is”**。
在**“属性/值”列中,选择“Null”**。
注意: 如果某个值/属性在某个条件中使用,那么此值/属性不能也是实体属性,除非该条件使用“Is Null”或“Is Not Null”比较。 对于 Student 实体类型请重复步骤 13 到 22,但需将条件设为 HireDate Is Null 和 EnrollmentDate Is Not Null。
每个层次结构一个表继承现已实现。
创建 Instructor 和 OfficeAssignment 之间的关联
在本节中,您将更改 Person 和 OfficeAssignment 之间的关联以使该关联在 Instructor 和 OfficeAssignment 之间建立。 采用这种方式更改关联符合该模型的意图。
创建 Instructor 和 OfficeAssignment 之间的关联
右击 Person 和 OfficeAssignment 之间的关联,然后选择**“删除”**。
右击实体设计器的设计图面上的空白区域,指向**“添加”,然后选择“关联”**。
对于关联的一**“端”,从“实体”下拉列表中选择 Instructor,然后从“重数”下拉列表中选择“1 (一个)”**。
对于关联的另一**“端”,从“实体”下拉列表中选择 OfficeAssignment,然后从“重数”下拉列表中选择“0..1 (零或一个)”**。
单击**“确定”**。
右击 Instructor 和 OfficeAssignment 之间的新关联,然后选择**“属性”**。
在“属性”窗口中,单击**“引用约束”,然后单击值列中显示的省略号按钮“(…)”**。
此时将显示“引用约束”对话框。
从**“主体”**下拉列表中选择 Instructor。
单击**“确定”**。
现在您已在 Instructor 和 OfficeAssignment 之间添加了一个关联。
构造用户界面
接下来,将向 CourseViewer 窗体添加一个用于加载和显示 Enrollment 窗体的按钮。 然后,将两个 ComboBox 控件和一个 ListBox 控件添加到 Enrollment 窗体中。 使用第一个 ComboBox 可以选择系。 使用第二个 ComboBox 可以根据在第一个 ComboBox 中选择的系而选择课程。 关于选定课程的学生和教师的列表将会显示在 ListBox 控件中。
构造用户界面
在**“解决方案资源管理器”中右击 CourseManager 项目,指向“添加”,然后选择“新建项”**。
此时将显示**“添加新项”**对话框。
选择**“Windows 窗体”,将窗体的名称设置为 Enrollment.vb 或 Enrollment.cs(取决于您所使用的语言),然后单击“添加”**。
新窗体即被添加到项目中,并在窗体设计器中打开。 窗体的名称设置为 Enrollment,文本设置为 Enrollment。
从工具箱将 ComboBox 控件拖入此窗体,然后在**“属性”**窗口中将其名称设置为 departmentList。
将另外一个 ComboBox 控件拖入此窗体,然后将其名称设置为 courseList。
从工具箱将 Listbox 控件拖入此窗体。 在**“属性”**窗口中将其名称设置为 studentList。
在**“解决方案资源管理器”**中,双击 CourseViewer.cs 或 CourseViewer.vb。
出现 CourseViewer 窗体的设计视图。
从工具箱将 Button 控件拖入 CourseViewer 窗体。
在**“属性”**窗口中,将按钮的名称设置为 viewEnrollment,然后将按钮的文本设置为 View Enrollment。
双击 viewEnrollment 按钮。
此时将打开 CourseViewer 窗体的代码隐藏文件。
将下面的代码添加到 viewEnrollment_click 事件处理程序中:
Dim enrollmentForm As New Enrollment() enrollmentForm.Visible = True
Enrollment enrollmentForm = new Enrollment(); enrollmentForm.Visible = true;
现在,就完成了用户界面。
查询概念模型
在此过程中,您将查询概念模型并将结果绑定到 Windows 窗体控件。 有关将对象绑定到控件的更多信息,请参见Binding Objects to Controls (Entity Framework)。
查询概念模型
在窗体设计器中打开 Enrollment 窗体,双击 Enrollment 窗体。
此时将打开 Enrollment 窗体的代码隐藏文件。
添加下面的 using (C#) 或 Imports (Visual Basic) 语句。
Imports System.Data.Objects Imports System.Data.Objects.DataClasses
using System.Data.Objects; using System.Data.Objects.DataClasses;
将表示数据上下文的属性添加到 Enrollment 类中:
' Create an ObjectContext instance based on SchoolEntity. Private schoolContext As SchoolEntities
// Create an ObjectContext instance based on SchoolEntities. private SchoolEntities schoolContext;
在 Enrollment_Load 事件处理程序中,添加相应的代码以初始化对象上下文,并将 ComboBox 控件绑定到返回所有系和相关课程信息的查询。
' Initialize the ObjectContext. schoolContext = New SchoolEntities() ' Define a query that returns all Department objects and ' related Course objects, ordered by name. Dim departmentQuery As ObjectQuery(Of Department) = _ schoolContext.Departments.Include("Courses") _ .OrderBy("it.Name") ' Bind the ComboBox control to the query, which is ' executed during data binding. Me.departmentList.DataSource = departmentQuery _ .Execute(MergeOption.OverwriteChanges) Me.departmentList.DisplayMember = "Name"
// Initialize the ObjectContext. schoolContext = new SchoolEntities(); // Define a query that returns all Department objects // and related Course objects, ordered by name. ObjectQuery<Department> departmentQuery = schoolContext.Departments.Include("Courses") .OrderBy("it.Name"); // Bind the ComboBox control to the query. this.departmentList.DataSource = departmentQuery .Execute(MergeOption.OverwriteChanges); this.departmentList.DisplayMember = "Name";
返回 Enrollment 窗体的设计视图,然后双击 departmentList ComboBox 控件。
departmentList_SelectedIndexChanged 事件处理程序在代码隐藏文件中创建。
将相应代码添加到事件处理程序,以将 courseList ComboBox 控件绑定到选定 Department 的 Course 属性。
' Get the object for the selected department Dim department As Department = CType(Me.departmentList _ .SelectedItem, Department) ' Bind the ComboBox control Course properties of ' the selected Department. courseList.DataSource = department.Courses courseList.DisplayMember = "Title"
// Get the object for the selected department. Department department = (Department)this.departmentList.SelectedItem; // Bind the ComboBox control to the Course // properties of the selected Department. courseList.DataSource = department.Courses; courseList.DisplayMember = "Title";
返回 Enrollment 窗体的设计视图,然后双击 courseList ComboBox 控件。
courseList_SelectedIndexChanged 事件处理程序在代码隐藏文件中创建。
将相应代码添加到事件处理程序,以在 ListBox 中显示学生列表。
Me.studentList.Items.Clear() ' Get the CourseID from the selected Course. Dim course As Course = CType(Me.courseList.SelectedItem, _ Course) Dim courseId As Integer = course.CourseID ' Get all StudentGrade objects for the supplied CourseID Dim studentQuery As Course = schoolContext.Courses.Where _ ("it.CourseID = @courseID", New ObjectParameter _ ("courseID", courseId)).Include("StudentGrades"). _ FirstOrDefault() ' Get all students for each StudentGrade For Each cg As StudentGrade In studentQuery.StudentGrades cg.PersonReference.Load() studentList.Items.Add(cg.Person.LastName + ", " + _ cg.Person.FirstName) Next studentList.Items.Add(" ") ' Get all Instructor types for the supplied CourseID Dim instructorQuery As Course = schoolContext.Courses. _ Where("it.CourseID = @courseID", New ObjectParameter _ ("courseID", courseId)).Include("People") _ .FirstOrDefault() ' Display each instructor for the specified Course For Each Instructor As Instructor In instructorQuery.People Me.studentList.Items.Add("Instructor: " + Instructor. _ LastName + ", " + Instructor.FirstName) Next
studentList.Items.Clear(); // Get the CourseID from the selected Course. Course course = (Course)courseList.SelectedItem; int courseId = course.CourseID; // Get all StudentGrade types for the supplied CourseID Course studentQuery = schoolContext.Courses.Where( "it.CourseID = @courseID", new ObjectParameter ("courseID", courseId)).Include("StudentGrades"). FirstOrDefault(); // Get all students for each StudentGrade foreach (StudentGrade sg in studentQuery.StudentGrades) { sg.PersonReference.Load(); studentList.Items.Add(sg.Person.LastName + ", " + sg.Person.FirstName); } studentList.Items.Add(" "); // Get all Instructor types for the supplied CourseID Course instructorQuery = schoolContext.Courses.Where( "it.CourseID = @courseID", new ObjectParameter ("courseID", courseId)).Include("People"). FirstOrDefault(); // Display each instructor for the specified course. foreach (Instructor instructor in instructorQuery.People. OfType<Instructor>()) { studentList.Items.Add("Instructor: " + instructor. LastName + ", " + instructor.FirstName); }
现在,就完成了应用程序。 按 Ctrl+F5 运行应用程序。 单击 View Enrollment 按钮以加载 Enrollment 窗体。 选定课程的课程注册情况和教师显示在 ListBox 中。
代码清单
本节列出了 Enrollment 窗体的代码隐藏文件的最终版本。
Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Public Class Enrollment
' Create an ObjectContext instance based on SchoolEntity.
Private schoolContext As SchoolEntities
Private Sub Enrollment_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' Initialize the ObjectContext.
schoolContext = New SchoolEntities()
' Define a query that returns all Department objects and
' related Course objects, ordered by name.
Dim departmentQuery As ObjectQuery(Of Department) = _
schoolContext.Departments.Include("Courses") _
.OrderBy("it.Name")
' Bind the ComboBox control to the query, which is
' executed during data binding.
Me.departmentList.DataSource = departmentQuery _
.Execute(MergeOption.OverwriteChanges)
Me.departmentList.DisplayMember = "Name"
End Sub
Private Sub departmentList_SelectedIndexChanged(ByVal sender _
As System.Object, ByVal e As System.EventArgs) Handles _
departmentList.SelectedIndexChanged
' Get the object for the selected department
Dim department As Department = CType(Me.departmentList _
.SelectedItem, Department)
' Bind the ComboBox control Course properties of
' the selected Department.
courseList.DataSource = department.Courses
courseList.DisplayMember = "Title"
End Sub
Private Sub courseList_SelectedIndexChanged(ByVal sender As _
System.Object, ByVal e As System.EventArgs) Handles _
courseList.SelectedIndexChanged
Me.studentList.Items.Clear()
' Get the CourseID from the selected Course.
Dim course As Course = CType(Me.courseList.SelectedItem, _
Course)
Dim courseId As Integer = course.CourseID
' Get all StudentGrade objects for the supplied CourseID
Dim studentQuery As Course = schoolContext.Courses.Where _
("it.CourseID = @courseID", New ObjectParameter _
("courseID", courseId)).Include("StudentGrades"). _
FirstOrDefault()
' Get all students for each StudentGrade
For Each cg As StudentGrade In studentQuery.StudentGrades
cg.PersonReference.Load()
studentList.Items.Add(cg.Person.LastName + ", " + _
cg.Person.FirstName)
Next
studentList.Items.Add(" ")
' Get all Instructor types for the supplied CourseID
Dim instructorQuery As Course = schoolContext.Courses. _
Where("it.CourseID = @courseID", New ObjectParameter _
("courseID", courseId)).Include("People") _
.FirstOrDefault()
' Display each instructor for the specified Course
For Each Instructor As Instructor In instructorQuery.People
Me.studentList.Items.Add("Instructor: " + Instructor. _
LastName + ", " + Instructor.FirstName)
Next
End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace CourseManager
{
public partial class Enrollment : Form
{
// Create an ObjectContext instance based on SchoolEntities.
private SchoolEntities schoolContext;
public Enrollment()
{
InitializeComponent();
}
private void Enrollment_Load(object sender, EventArgs e)
{
// Initialize the ObjectContext.
schoolContext = new SchoolEntities();
// Define a query that returns all Department objects
// and related Course objects, ordered by name.
ObjectQuery<Department> departmentQuery =
schoolContext.Departments.Include("Courses")
.OrderBy("it.Name");
// Bind the ComboBox control to the query.
this.departmentList.DataSource = departmentQuery
.Execute(MergeOption.OverwriteChanges);
this.departmentList.DisplayMember = "Name";
}
private void departmentList_SelectedIndexChanged(object sender,
EventArgs e)
{
// Get the object for the selected department.
Department department =
(Department)this.departmentList.SelectedItem;
// Bind the ComboBox control to the Course
// properties of the selected Department.
courseList.DataSource = department.Courses;
courseList.DisplayMember = "Title";
}
private void courseList_SelectedIndexChanged(object sender,
EventArgs e)
{
studentList.Items.Clear();
// Get the CourseID from the selected Course.
Course course = (Course)courseList.SelectedItem;
int courseId = course.CourseID;
// Get all StudentGrade types for the supplied CourseID
Course studentQuery = schoolContext.Courses.Where(
"it.CourseID = @courseID", new ObjectParameter
("courseID", courseId)).Include("StudentGrades").
FirstOrDefault();
// Get all students for each StudentGrade
foreach (StudentGrade sg in studentQuery.StudentGrades)
{
sg.PersonReference.Load();
studentList.Items.Add(sg.Person.LastName + ", " +
sg.Person.FirstName);
}
studentList.Items.Add(" ");
// Get all Instructor types for the supplied CourseID
Course instructorQuery = schoolContext.Courses.Where(
"it.CourseID = @courseID", new ObjectParameter
("courseID", courseId)).Include("People").
FirstOrDefault();
// Display each instructor for the specified course.
foreach (Instructor instructor in instructorQuery.People.
OfType<Instructor>())
{
studentList.Items.Add("Instructor: " + instructor.
LastName + ", " + instructor.FirstName);
}
}
}
}
后续步骤
您已在概念模型中成功实现了每个层次结构一个表继承。 有关如何通过每个层次结构一个表继承定义概念模型的更多信息,请参见How to: Define a Model with Table-per-Hierarchy Inheritance。 有关如何生成使用实体框架的应用程序的更多信息,请参见ADO.NET Entity Framework。