使用 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 控件来处理相关数据。 在导航属性中显示了多个层次结构级别和已编辑的数据。 在本教程中,你将通过添加和删除关系以及添加与现有实体有关系的新实体来继续使用相关数据。

你将创建一个页面,用于添加分配给部门的课程。 这些部门已经存在,当你创建新课程时,你将在它与现有部门之间建立关系。

Internet Explorer 窗口的屏幕截图,其中显示了“添加课程”视图,其中包含“ID”、“标题”和“学分”文本字段以及“部门”下拉列表。

你还将通过为课程分配讲师 (在选择) 的两个实体之间添加关系,或者从课程中删除讲师 (删除你选择) 的两个实体之间的关系,来创建一个使用多对多关系的页面。 在数据库中,在讲师和课程之间添加关系会导致将新行添加到 CourseInstructor 关联表;删除关系涉及从 CourseInstructor 关联表中删除行。 但是,可以通过设置导航属性在实体框架中执行此操作,而无需显式引用 CourseInstructor 表。

Internet Explorer 窗口的屏幕截图,其中显示了“将讲师分配到课程”或“从课程中删除”视图。

将具有关系的实体添加到现有实体

创建一个名为 CoursesAdd.aspx 的新网页,该网页使用 Site.Master 母版页,并将以下标记添加到名为 的ContentContent2控件:

<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实体属性的值CourseDropDownList

实体框架将负责将本课程添加到 Courses 关联 Department 实体的导航属性。 它还会将 部门添加到 Department 实体的 Course 导航属性。

运行页面。

Internet Explorer 窗口的屏幕截图,其中显示了“添加课程”视图,其中包含“ID”、“标题”和“学分”文本字段以及“部门”下拉列表。

输入 ID、职务、学分数,然后选择一个部门,然后单击“ 插入”。

运行 Courses.aspx 页,并选择同一部门以查看新课程。

Image03

处理多对多关系

实体集与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();
}

运行页面。

Internet Explorer 窗口的屏幕截图,其中显示了“将讲师分配到课程”或“从课程中删除”视图,其中包含相应的下拉列表。

选择讲师。 “ 分配课程” 下拉列表显示讲师未教授的课程,“ 删除课程 ”下拉列表显示讲师已分配到的课程。 在 “分配课程 ”部分中,选择一门课程,然后单击“ 分配”。 课程将移动到 “删除课程 ”下拉列表。 在“删除课程”部分选择 一门课程 ,然后单击“ 删除”。 课程将移动到“ 分配课程 ”下拉列表。

现在,你已经了解了使用相关数据的更多方法。 在以下教程中,你将了解如何在数据模型中使用继承来提高应用程序的可维护性。