消費者入門 Entity Framework 4.0 Database First 和 ASP.NET 4 Web Form - 第 4 部分

By Tom Dykstra

Contoso University 範例 Web 應用程式示範如何使用 Entity Framework 4.0 和 Visual Studio 2010 建立 ASP.NET Web Forms應用程式。 如需教學課程系列的相關資訊,請參閱 系列中的第一個教學課程

在上一個教學課程中, EntityDataSource 您已使用 控制項來篩選、排序和群組資料。 在本教學課程中,您將顯示及更新相關資料。

您將建立顯示講師清單的 Instructors 頁面。 當您選取講師時,您會看到該講師所教授的課程清單。 當您選取課程時,您會看到課程的詳細資料,以及課程中註冊的學生清單。 您可以編輯講師名稱、雇用日期和辦公室工作分派。 辦公室指派是您透過導覽屬性存取的個別實體集。

您可以將主要資料連結至標記或程式碼中的詳細資料。 在本教學課程的這個部分中,您將使用這兩種方法。

Image01

建立名為 Instructors.aspx 的新網頁,以使用 Site.Master 主版頁面,並將下列標記新增至 Content 名為 的 Content2 控制項:

<h2>Instructors</h2>
    <div>
        <asp:EntityDataSource ID="InstructorsEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
            EntitySetName="People"
            Where="it.HireDate is not null" Include="OfficeAssignment" EnableUpdate="True">
        </asp:EntityDataSource>
    </div>

此標記會建立控制項 EntityDataSource ,以選取講師並啟用更新。 元素 div 會設定標記以在左側呈現,以便稍後在右側加入資料行。

EntityDataSource 標記和結尾 </div> 標記之間,新增下列標記,以建立 GridView 控制項,以及 Label 您將用於錯誤訊息的控制項:

<asp:GridView ID="InstructorsGridView" runat="server" AllowPaging="True" AllowSorting="True"
            AutoGenerateColumns="False" DataKeyNames="PersonID" DataSourceID="InstructorsEntityDataSource"
            OnSelectedIndexChanged="InstructorsGridView_SelectedIndexChanged" 
            SelectedRowStyle-BackColor="LightGray" 
            onrowupdating="InstructorsGridView_RowUpdating">
            <Columns>
                <asp:CommandField ShowSelectButton="True" ShowEditButton="True" />
                <asp:TemplateField HeaderText="Name" SortExpression="LastName">
                    <ItemTemplate>
                        <asp:Label ID="InstructorLastNameLabel" runat="server" Text='<%# Eval("LastName") %>'></asp:Label>,
                        <asp:Label ID="InstructorFirstNameLabel" runat="server" Text='<%# Eval("FirstMidName") %>'></asp:Label>
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox ID="InstructorLastNameTextBox" runat="server" Text='<%# Bind("FirstMidName") %>' Width="7em"></asp:TextBox>
                        <asp:TextBox ID="InstructorFirstNameTextBox" runat="server" Text='<%# Bind("LastName") %>' Width="7em"></asp:TextBox>
                    </EditItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Hire Date" SortExpression="HireDate">
                    <ItemTemplate>
                        <asp:Label ID="InstructorHireDateLabel" runat="server" Text='<%# Eval("HireDate", "{0:d}") %>'></asp:Label>
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox ID="InstructorHireDateTextBox" runat="server" Text='<%# Bind("HireDate", "{0:d}") %>' Width="7em"></asp:TextBox>
                    </EditItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Office Assignment" SortExpression="OfficeAssignment.Location">
                    <ItemTemplate>
                        <asp:Label ID="InstructorOfficeLabel" runat="server" Text='<%# Eval("OfficeAssignment.Location") %>'></asp:Label>
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox ID="InstructorOfficeTextBox" runat="server" 
                        Text='<%# Eval("OfficeAssignment.Location") %>' Width="7em"
                        oninit="InstructorOfficeTextBox_Init"></asp:TextBox>
                    </EditItemTemplate>
                </asp:TemplateField>
            </Columns>
            <SelectedRowStyle BackColor="LightGray"></SelectedRowStyle>
        </asp:GridView>
        <asp:Label ID="ErrorMessageLabel" runat="server" Text="" Visible="false" ViewStateMode="Disabled"></asp:Label>

GridView 控制項會啟用資料列選取、醒目提示具有淺灰色背景色彩的選取資料列,並指定處理常式 (您稍後會為 SelectedIndexChangedUpdating 事件建立) 。 它也會指定 PersonIDDataKeyNames 屬性,以便將所選資料列的索引鍵值傳遞至稍後將新增的另一個控制項。

最後一個資料行包含講師的辦公室指派,儲存在實體的 Person 導覽屬性中,因為它來自相關聯的實體。 請注意, EditItemTemplate 元素會 Eval 指定 ,而不是 Bind ,因為 GridView 控制項無法直接系結至導覽屬性以更新它們。 您將在程式碼中更新辦公室指派。 若要這樣做,您需要控制項的 TextBox 參考,而且您會在控制項的 Init 事件中 TextBox 取得並儲存該參考。

GridView遵循 控制項是 Label 用於錯誤訊息的控制項。 控制項的 Visible 屬性為 false ,且檢視狀態已關閉,因此只有在程式碼讓標籤能夠顯示以回應錯誤時才會顯示。

開啟 Instructors.aspx.cs 檔案,並新增下列 using 語句:

using ContosoUniversity.DAL;

在部分類別名稱宣告之後立即新增私用類別欄位,以保存 Office 指派文字方塊的參考。

private TextBox instructorOfficeTextBox;

為事件處理常式新增存根, SelectedIndexChanged 以便稍後新增程式碼。 此外,新增 Office 工作分派 TextBox 控制項事件的 Init 處理常式,以便儲存控制項的 TextBox 參考。 您將使用此參考來取得使用者輸入的值,以更新與導覽屬性相關聯的實體。

protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
}

protected void InstructorOfficeTextBox_Init(object sender, EventArgs e)
{
    instructorOfficeTextBox = sender as TextBox;
}

您將使用 GridView 控制項的事件 Updating 來更新 Location 相關聯 OfficeAssignment 實體的 屬性。 為 Updating 事件新增下列處理常式:

protected void InstructorsGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    using (var context = new SchoolEntities())
    {
        var instructorBeingUpdated = Convert.ToInt32(e.Keys[0]);
        var officeAssignment = (from o in context.OfficeAssignments
                                where o.InstructorID == instructorBeingUpdated
                                select o).FirstOrDefault();

        try
        {
            if (String.IsNullOrWhiteSpace(instructorOfficeTextBox.Text) == false)
            {
                if (officeAssignment == null)
                {
                    context.OfficeAssignments.AddObject(OfficeAssignment.CreateOfficeAssignment(instructorBeingUpdated, instructorOfficeTextBox.Text, null));
                }
                else
                {
                    officeAssignment.Location = instructorOfficeTextBox.Text;
                }
            }
            else
            {
                if (officeAssignment != null)
                {
                    context.DeleteObject(officeAssignment);
                }
            }
            context.SaveChanges();
        }
        catch (Exception)
        {
            e.Cancel = true;
            ErrorMessageLabel.Visible = true;
            ErrorMessageLabel.Text = "Update failed.";
            //Add code to log the error.
        }
    }
}

當使用者按一下資料 GridView 列中的[更新] 時,就會執行此程式碼。 程式碼會使用LINQ to Entities,從事件引數擷 PersonIDOfficeAssignment 與目前 Person 實體相關聯的實體。

程式碼接著會根據 控制項中的 InstructorOfficeTextBox 值,採取下列其中一個動作:

  • 如果文字方塊有值,而且沒有 OfficeAssignment 要更新的實體,則會建立一個實體。
  • 如果文字方塊有值且有 OfficeAssignment 實體,則會更新 Location 屬性值。
  • 如果文字方塊是空的,而且實體 OfficeAssignment 存在,則會刪除實體。

在此之後,它會將變更儲存至資料庫。 如果發生例外狀況,它會顯示錯誤訊息。

執行頁面。

Image02

按一下 [編輯 ],所有欄位都會變更為文字方塊。

Image03

變更上述任何值,包括 Office 工作分派。 按一下 [更新 ],您會看到清單中反映的變更。

每個講師都可以教導一或多個課程,因此您將新增 EntityDataSource 控制項和 GridView 控制項,以列出與講師控制項中選取之講師 GridView 相關聯的課程。 若要建立課程實體的標題和 EntityDataSource 控制項,請在錯誤訊息 Label 控制項與結尾 </div> 標記之間新增下列標記:

<h3>Courses Taught</h3>
        <asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
            EntitySetName="Courses" 
            Where="@PersonID IN (SELECT VALUE instructor.PersonID FROM it.People AS instructor)">
            <WhereParameters>
                <asp:ControlParameter ControlID="InstructorsGridView" Type="Int32" Name="PersonID" PropertyName="SelectedValue" />
            </WhereParameters>
        </asp:EntityDataSource>

參數 Where 包含在 控制項中 InstructorsGridView 選取其資料列之講師的 值 PersonID 。 屬性 Where 包含子選取命令,這個命令會從 Course 實體的 People 導覽屬性取得所有相關聯的 Person 實體,而且只有在其中一個相關聯的 Person 實體包含選取 PersonID 的值時, Course 才會選取實體。

若要建立 GridView control.,請在結尾 </div> 標籤) 之前,緊接在 (控制項後面 CoursesEntityDataSource 新增下列標記) :

<asp:GridView ID="CoursesGridView" runat="server" 
            DataSourceID="CoursesEntityDataSource"
            AllowSorting="True" AutoGenerateColumns="False"
            SelectedRowStyle-BackColor="LightGray" 
            DataKeyNames="CourseID">
            <EmptyDataTemplate>
                <p>No courses found.</p>
            </EmptyDataTemplate>
            <Columns>
                <asp:CommandField ShowSelectButton="True" />
                <asp:BoundField DataField="CourseID" HeaderText="ID" ReadOnly="True" SortExpression="CourseID" />
                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
                <asp:TemplateField HeaderText="Department" SortExpression="DepartmentID">
                    <ItemTemplate>
                        <asp:Label ID="GridViewDepartmentLabel" runat="server" Text='<%# Eval("Department.Name") %>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>

因為如果未選取任何講師,則不會顯示任何課程, EmptyDataTemplate 因此會包含元素。

執行頁面。

Image04

選取已指派一或多個課程的講師,課程或課程會出現在清單中。 (注意:雖然資料庫架構允許多個課程,但資料庫中所提供的測試資料中,沒有講師實際上沒有一個以上的課程。您可以使用 [伺服器總 管] 視窗或 CoursesAdd.aspx 頁面,自行將課程新增至資料庫,您將在稍後的 tutorial.)

Image05

控制項 CoursesGridView 只會顯示幾個課程欄位。 若要顯示課程的所有詳細資料,您將針對使用者選取的課程使用 DetailsView 控制項。 在 Instructors.aspx中,在結尾 </div> 標記後面新增下列標記 (確定您將此標記放在結尾 div 標籤 之後 ,而不是) 之前:

<div>
        <h3>Course Details</h3>
        <asp:EntityDataSource ID="CourseDetailsEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False" 
            EntitySetName="Courses"
            AutoGenerateWhereClause="False" Where="it.CourseID = @CourseID" Include="Department,OnlineCourse,OnsiteCourse,StudentGrades.Person"
            OnSelected="CourseDetailsEntityDataSource_Selected">
            <WhereParameters>
                <asp:ControlParameter ControlID="CoursesGridView" Type="Int32" Name="CourseID" PropertyName="SelectedValue" />
            </WhereParameters>
        </asp:EntityDataSource>
        <asp:DetailsView ID="CourseDetailsView" runat="server" AutoGenerateRows="False"
            DataSourceID="CourseDetailsEntityDataSource">
            <EmptyDataTemplate>
                <p>
                    No course selected.</p>
            </EmptyDataTemplate>
            <Fields>
                <asp:BoundField DataField="CourseID" HeaderText="ID" ReadOnly="True" SortExpression="CourseID" />
                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
                <asp:BoundField DataField="Credits" HeaderText="Credits" SortExpression="Credits" />
                <asp:TemplateField HeaderText="Department">
                    <ItemTemplate>
                        <asp:Label ID="DetailsViewDepartmentLabel" runat="server" Text='<%# Eval("Department.Name") %>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Location">
                    <ItemTemplate>
                        <asp:Label ID="LocationLabel" runat="server" Text='<%# Eval("OnsiteCourse.Location") %>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="URL">
                    <ItemTemplate>
                        <asp:Label ID="URLLabel" runat="server" Text='<%# Eval("OnlineCourse.URL") %>'></asp:Label>
                    </ItemTemplate>
                </asp:TemplateField>
            </Fields>
        </asp:DetailsView>
    </div>

此標記會 EntityDataSource 建立系結至實體集的 Courses 控制項。 屬性 Where 會使用 CourseID 課程式控制件中所選資料列的值來選取課程 GridView 。 標記會指定事件的處理常式 Selected ,稍後您將用來顯示學生成績,這是階層中另一個較低的層級。

Instructors.aspx.cs中,為 CourseDetailsEntityDataSource_Selected 方法建立下列存根。 (您稍後會在教學課程中填入此存根;現在,您需要它,讓頁面編譯並執行.)

protected void CourseDetailsEntityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
}

執行頁面。

Image06

一開始沒有課程詳細資料,因為未選取任何課程。 選取已指派課程的講師,然後選取課程以查看詳細資料。

Image07

最後,您想要顯示所有已註冊的學生及其所選課程的成績。 若要這樣做,您將使用 Selected 系結至課程 DetailsViewEntityDataSource 控制項事件。

Instructors.aspx中,在 控制項後面 DetailsView 新增下列標記:

<h3>Student Grades</h3>
        <asp:ListView ID="GradesListView" runat="server">
            <EmptyDataTemplate>
                <p>No student grades found.</p>
            </EmptyDataTemplate>
            <LayoutTemplate>
                <table border="1" runat="server" id="itemPlaceholderContainer">
                    <tr runat="server">
                        <th runat="server">
                            Name
                        </th>
                        <th runat="server">
                            Grade
                        </th>
                    </tr>
                    <tr id="itemPlaceholder" runat="server">
                    </tr>
                </table>
            </LayoutTemplate>
            <ItemTemplate>
                <tr>
                    <td>
                        <asp:Label ID="StudentLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>' />,
                        <asp:Label ID="StudentFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>' />
                    </td>
                    <td>
                        <asp:Label ID="StudentGradeLabel" runat="server" Text='<%# Eval("Grade") %>' />
                    </td>
                </tr>
            </ItemTemplate>
        </asp:ListView>

此標記會 ListView 建立控制項,以顯示學生清單及其所選課程的成績。 未指定資料來源,因為您將在程式碼中系結 控制項。 元素 EmptyDataTemplate 會在未選取課程時提供要顯示的訊息,在此情況下,沒有任何學生可顯示。 專案 LayoutTemplate 會建立 HTML 資料表來顯示清單,並 ItemTemplate 指定要顯示的資料行。 學生識別碼和學生成績來自 StudentGrade 實體,而學生名稱來自 Person Entity Framework 在實體的 StudentGrade 導覽屬性中 Person 提供的實體。

Instructors.aspx.cs中,以下列程式碼取代 stubbed-out CourseDetailsEntityDataSource_Selected 方法:

protected void CourseDetailsEntityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
    var course = e.Results.Cast<Course>().FirstOrDefault();
    if (course != null)
    {
        var studentGrades = course.StudentGrades.ToList();
        GradesListView.DataSource = studentGrades;
        GradesListView.DataBind();
    }
}

此事件的事件引數會以集合的形式提供選取的資料,如果未選取任何專案,則會有零個專案,如果 Course 選取實體,則會有一個專案。 Course如果選取實體,程式碼會 First 使用 方法將集合轉換成單一物件。 然後,它會從導覽屬性取得 StudentGrade 實體、將它們轉換成集合,並將控制項系結 GradesListView 至集合。

這足以顯示成績,但您想要確定第一次顯示頁面時,以及每次未選取課程時,就會顯示空白資料範本中的訊息。 若要這樣做,請建立下列方法,您將從兩個位置呼叫:

private void ClearStudentGradesDataSource()
{
    var emptyStudentGradesList = new List<StudentGrade>();
    GradesListView.DataSource = emptyStudentGradesList;
    GradesListView.DataBind();
}

Page_Load 方法呼叫這個新方法,以在第一次顯示頁面時顯示空白資料範本。 並從 方法呼叫它 InstructorsGridView_SelectedIndexChanged ,因為選取講師時會引發該事件,這表示新課程會載入課程 GridView 控制項,而且尚未選取任何課程。 以下是兩個呼叫:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        ClearStudentGradesDataSource();                
    }
}
protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
    ClearStudentGradesDataSource();
}

執行頁面。

Image08

選取已指派課程的講師,然後選取課程。

Image09

您現在已看到一些使用相關資料的方式。 在下列教學課程中,您將瞭解如何新增現有實體之間的關聯性、如何移除關聯性,以及如何新增具有現有實體關聯性的新實體。