使用 Entity Framework 4.0 Database First 和 ASP.NET 4 Web Forms 入门 - 第 5 部分
作者 :Tom Dykstra
Contoso University 示例 Web 应用程序演示如何使用 Entity Framework 4.0 和 Visual Studio 2010 创建 ASP.NET Web Forms应用程序。 有关教程系列的信息,请参阅 系列中的第一个教程
处理相关数据,继续
在上一教程中,你开始使用 EntityDataSource
控件来处理相关数据。 在导航属性中显示了多个层次结构级别和已编辑的数据。 在本教程中,你将通过添加和删除关系以及添加与现有实体有关系的新实体来继续使用相关数据。
你将创建一个页面,用于添加分配给部门的课程。 这些部门已经存在,当你创建新课程时,你将在它与现有部门之间建立关系。
你还将通过为课程分配讲师 (在选择) 的两个实体之间添加关系,或者从课程中删除讲师 (删除你选择) 的两个实体之间的关系,来创建一个使用多对多关系的页面。 在数据库中,在讲师和课程之间添加关系会导致将新行添加到 CourseInstructor
关联表;删除关系涉及从 CourseInstructor
关联表中删除行。 但是,可以通过设置导航属性在实体框架中执行此操作,而无需显式引用 CourseInstructor
表。
将具有关系的实体添加到现有实体
创建一个名为 CoursesAdd.aspx 的新网页,该网页使用 Site.Master 母版页,并将以下标记添加到名为 的Content
Content2
控件:
<h2>Add Courses</h2>
<asp:EntityDataSource ID="CoursesEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EntitySetName="Courses"
EnableInsert="True" EnableDelete="True" >
</asp:EntityDataSource>
<asp:DetailsView ID="CoursesDetailsView" runat="server" AutoGenerateRows="False"
DataSourceID="CoursesEntityDataSource" DataKeyNames="CourseID"
DefaultMode="Insert" oniteminserting="CoursesDetailsView_ItemInserting">
<Fields>
<asp:BoundField DataField="CourseID" HeaderText="ID" />
<asp:BoundField DataField="Title" HeaderText="Title" />
<asp:BoundField DataField="Credits" HeaderText="Credits" />
<asp:TemplateField HeaderText="Department">
<InsertItemTemplate>
<asp:EntityDataSource ID="DepartmentsEntityDataSource" runat="server" ConnectionString="name=SchoolEntities"
DefaultContainerName="SchoolEntities" EnableDelete="True" EnableFlattening="False"
EntitySetName="Departments" EntityTypeFilter="Department">
</asp:EntityDataSource>
<asp:DropDownList ID="DepartmentsDropDownList" runat="server" DataSourceID="DepartmentsEntityDataSource"
DataTextField="Name" DataValueField="DepartmentID"
oninit="DepartmentsDropDownList_Init">
</asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
此标记创建一个 EntityDataSource
控件,用于选择课程、启用插入,并指定事件的处理程序 Inserting
。 创建新实体时Course
,你将使用 处理程序更新Department
导航属性。
标记还会创建用于 DetailsView
添加新 Course
实体的控件。 标记对 Course
实体属性使用绑定字段。 必须输入值, CourseID
因为这不是系统生成的 ID 字段。 相反,它是在创建课程时必须手动指定的课程编号。
使用导航属性的模板字段, Department
因为导航属性不能与控件一起使用 BoundField
。 模板字段提供用于选择部门的下拉列表。 下拉列表通过使用 Eval
而不是 Bind
绑定到Departments
实体集,同样,因为无法直接绑定导航属性以更新它们。 为控件Init
的事件指定处理程序DropDownList
,以便可以存储对控件的引用,供更新DepartmentID
外键的代码使用。
在分部类声明之后的 CoursesAdd.aspx.cs 中,添加类字段以保存对控件的 DepartmentsDropDownList
引用:
private DropDownList departmentDropDownList;
为 DepartmentsDropDownList
控件 Init
的事件添加处理程序,以便可以存储对控件的引用。 这样,便可以获取用户输入的值,并使用它来更新 DepartmentID
实体的值 Course
。
protected void DepartmentsDropDownList_Init(object sender, EventArgs e)
{
departmentDropDownList = sender as DropDownList;
}
为 DetailsView
控件 Inserting
的事件添加处理程序:
protected void CoursesDetailsView_ItemInserting(object sender, DetailsViewInsertEventArgs e)
{
var departmentID = Convert.ToInt32(departmentDropDownList.SelectedValue);
e.Values["DepartmentID"] = departmentID;
}
当用户单击 Insert
时,在 Inserting
插入新记录之前引发 事件。 处理程序中的代码从 控件中获取 DepartmentID
,并使用它来设置将用于DepartmentID
实体属性的值Course
。DropDownList
实体框架将负责将本课程添加到 Courses
关联 Department
实体的导航属性。 它还会将 部门添加到 Department
实体的 Course
导航属性。
运行页面。
输入 ID、职务、学分数,然后选择一个部门,然后单击“ 插入”。
运行 Courses.aspx 页,并选择同一部门以查看新课程。
处理多对多关系
实体集与People
实体集之间的关系Courses
是多对多关系。 实体 Course
具有名为 的 People
导航属性,该属性可以包含零个、一个或多个相关 Person
实体, (表示分配用于教授该课程的讲师) 。 Person
实体具有名为 的Courses
导航属性,该属性可以包含零个、一个或多个相关Course
实体, (表示分配讲师教授) 的课程。 一名讲师可能教授多个课程,一门课程可能由多个讲师教授。 在本演练的本节中,你将通过更新相关实体的导航属性来添加和删除 和 Course
实体之间的关系Person
。
创建一个名为 InstructorsCourses.aspx 的新网页,该网页使用 Site.Master 母版页,并将以下标记添加到 Content
名为 的 Content2
控件:
<h2>Assign Instructors to Courses or Remove from Courses</h2>
<br />
<asp:EntityDataSource ID="InstructorsEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EntitySetName="People"
Where="it.HireDate is not null" Select="it.LastName + ', ' + it.FirstMidName AS Name, it.PersonID">
</asp:EntityDataSource>
Select an Instructor:
<asp:DropDownList ID="InstructorsDropDownList" runat="server" DataSourceID="InstructorsEntityDataSource"
AutoPostBack="true" DataTextField="Name" DataValueField="PersonID"
OnSelectedIndexChanged="InstructorsDropDownList_SelectedIndexChanged"
OnDataBound="InstructorsDropDownList_DataBound">
</asp:DropDownList>
<h3>
Assign a Course</h3>
<br />
Select a Course:
<asp:DropDownList ID="UnassignedCoursesDropDownList" runat="server"
DataTextField="Title" DataValueField="CourseID">
</asp:DropDownList>
<br />
<asp:Button ID="AssignCourseButton" runat="server" Text="Assign" OnClick="AssignCourseButton_Click" />
<br />
<asp:Label ID="CourseAssignedLabel" runat="server" Visible="false" Text="Assignment successful"></asp:Label>
<br />
<h3>
Remove a Course</h3>
<br />
Select a Course:
<asp:DropDownList ID="AssignedCoursesDropDownList" runat="server"
DataTextField="title" DataValueField="courseiD">
</asp:DropDownList>
<br />
<asp:Button ID="RemoveCourseButton" runat="server" Text="Remove" OnClick="RemoveCourseButton_Click" />
<br />
<asp:Label ID="CourseRemovedLabel" runat="server" Visible="false" Text="Removal successful"></asp:Label>
此标记创建一个EntityDataSource
控件,用于检索讲师的Person
实体的名称和PersonID
。 控件 DropDrownList
绑定到控件 EntityDataSource
。 控件 DropDownList
指定 事件的处理程序 DataBound
。 你将使用此处理程序对显示课程的两个下拉列表进行数据绑定。
标记还会创建以下控件组,用于将课程分配给所选讲师:
- 用于
DropDownList
选择要分配的课程的控件。 此控件将填充当前未分配给所选讲师的课程。 - 用于
Button
启动分配的控件。 - 一个
Label
控件,用于在分配失败时显示错误消息。
最后,标记还会创建一组控件,用于从所选讲师中删除课程。
在 InstructorsCourses.aspx.cs 中,添加 using 语句:
using ContosoUniversity.DAL;
添加用于填充显示课程的两个下拉列表的方法:
private void PopulateDropDownLists()
{
using (var context = new SchoolEntities())
{
var allCourses = (from c in context.Courses
select c).ToList();
var instructorID = Convert.ToInt32(InstructorsDropDownList.SelectedValue);
var instructor = (from p in context.People.Include("Courses")
where p.PersonID == instructorID
select p).First();
var assignedCourses = instructor.Courses.ToList();
var unassignedCourses = allCourses.Except(assignedCourses.AsEnumerable()).ToList();
UnassignedCoursesDropDownList.DataSource = unassignedCourses;
UnassignedCoursesDropDownList.DataBind();
UnassignedCoursesDropDownList.Visible = true;
AssignedCoursesDropDownList.DataSource = assignedCourses;
AssignedCoursesDropDownList.DataBind();
AssignedCoursesDropDownList.Visible = true;
}
}
此代码从Courses
实体集中获取所有课程,并从所选讲师的实体的Person
导航属性获取课程Courses
。 然后,它确定分配给该讲师的课程,并相应地填充下拉列表。
为 Assign
按钮 Click
的事件添加处理程序:
protected void AssignCourseButton_Click(object sender, EventArgs e)
{
using (var context = new SchoolEntities())
{
var instructorID = Convert.ToInt32(InstructorsDropDownList.SelectedValue);
var instructor = (from p in context.People
where p.PersonID == instructorID
select p).First();
var courseID = Convert.ToInt32(UnassignedCoursesDropDownList.SelectedValue);
var course = (from c in context.Courses
where c.CourseID == courseID
select c).First();
instructor.Courses.Add(course);
try
{
context.SaveChanges();
PopulateDropDownLists();
CourseAssignedLabel.Text = "Assignment successful.";
}
catch (Exception)
{
CourseAssignedLabel.Text = "Assignment unsuccessful.";
//Add code to log the error.
}
CourseAssignedLabel.Visible = true;
}
}
此代码获取 Person
所选讲师的实体,获取 Course
所选课程的实体,并将所选课程添加到 Courses
讲师实体的 Person
导航属性。 然后,它会保存对数据库的更改并重新填充下拉列表,以便可以立即看到结果。
为 Remove
按钮 Click
的事件添加处理程序:
protected void RemoveCourseButton_Click(object sender, EventArgs e)
{
using (var context = new SchoolEntities())
{
var instructorID = Convert.ToInt32(InstructorsDropDownList.SelectedValue);
var instructor = (from p in context.People
where p.PersonID == instructorID
select p).First();
var courseID = Convert.ToInt32(AssignedCoursesDropDownList.SelectedValue);
var courses = instructor.Courses;
var courseToRemove = new Course();
foreach (Course c in courses)
{
if (c.CourseID == courseID)
{
courseToRemove = c;
break;
}
}
try
{
courses.Remove(courseToRemove);
context.SaveChanges();
PopulateDropDownLists();
CourseRemovedLabel.Text = "Removal successful.";
}
catch (Exception)
{
CourseRemovedLabel.Text = "Removal unsuccessful.";
//Add code to log the error.
}
CourseRemovedLabel.Visible = true;
}
}
此代码获取Person
所选讲师的实体,获取Course
所选课程的实体,并从实体的Courses
导航属性中删除所选课程Person
。 然后,它会保存对数据库的更改并重新填充下拉列表,以便可以立即看到结果。
向 方法添加代码Page_Load
,以确保在没有要报告错误时不显示错误消息,并为讲师下拉列表的 和 SelectedIndexChanged
事件添加处理程序DataBound
以填充课程下拉列表:
protected void Page_Load(object sender, EventArgs e)
{
CourseAssignedLabel.Visible = false;
CourseRemovedLabel.Visible = false;
}
protected void InstructorsDropDownList_DataBound(object sender, EventArgs e)
{
PopulateDropDownLists();
}
protected void InstructorsDropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
PopulateDropDownLists();
}
运行页面。
选择讲师。 “ 分配课程” 下拉列表显示讲师未教授的课程,“ 删除课程 ”下拉列表显示讲师已分配到的课程。 在 “分配课程 ”部分中,选择一门课程,然后单击“ 分配”。 课程将移动到 “删除课程 ”下拉列表。 在“删除课程”部分选择 一门课程 ,然后单击“ 删除”。 课程将移动到“ 分配课程 ”下拉列表。
现在,你已经了解了使用相关数据的更多方法。 在以下教程中,你将了解如何在数据模型中使用继承来提高应用程序的可维护性。