消費者入門 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 頁面。 當您選取講師時,您會看到該講師所教授的課程清單。 當您選取課程時,您會看到課程的詳細資料,以及課程中註冊的學生清單。 您可以編輯講師名稱、雇用日期和辦公室工作分派。 辦公室指派是您透過導覽屬性存取的個別實體集。
您可以將主要資料連結至標記或程式碼中的詳細資料。 在本教學課程的這個部分中,您將使用這兩種方法。
在 GridView 控制項中顯示和更新相關實體
建立名為 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
控制項會啟用資料列選取、醒目提示具有淺灰色背景色彩的選取資料列,並指定處理常式 (您稍後會為 SelectedIndexChanged
和 Updating
事件建立) 。 它也會指定 PersonID
DataKeyNames
屬性,以便將所選資料列的索引鍵值傳遞至稍後將新增的另一個控制項。
最後一個資料行包含講師的辦公室指派,儲存在實體的 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,從事件引數擷 PersonID
取 OfficeAssignment
與目前 Person
實體相關聯的實體。
程式碼接著會根據 控制項中的 InstructorOfficeTextBox
值,採取下列其中一個動作:
- 如果文字方塊有值,而且沒有
OfficeAssignment
要更新的實體,則會建立一個實體。 - 如果文字方塊有值且有
OfficeAssignment
實體,則會更新Location
屬性值。 - 如果文字方塊是空的,而且實體
OfficeAssignment
存在,則會刪除實體。
在此之後,它會將變更儲存至資料庫。 如果發生例外狀況,它會顯示錯誤訊息。
執行頁面。
按一下 [編輯 ],所有欄位都會變更為文字方塊。
變更上述任何值,包括 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
因此會包含元素。
執行頁面。
選取已指派一或多個課程的講師,課程或課程會出現在清單中。 (注意:雖然資料庫架構允許多個課程,但資料庫中所提供的測試資料中,沒有講師實際上沒有一個以上的課程。您可以使用 [伺服器總 管] 視窗或 CoursesAdd.aspx 頁面,自行將課程新增至資料庫,您將在稍後的 tutorial.)
控制項 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)
{
}
執行頁面。
一開始沒有課程詳細資料,因為未選取任何課程。 選取已指派課程的講師,然後選取課程以查看詳細資料。
使用 EntityDataSource 「選取」事件來顯示相關資料
最後,您想要顯示所有已註冊的學生及其所選課程的成績。 若要這樣做,您將使用 Selected
系結至課程 DetailsView
的 EntityDataSource
控制項事件。
在 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();
}
執行頁面。
選取已指派課程的講師,然後選取課程。
您現在已看到一些使用相關資料的方式。 在下列教學課程中,您將瞭解如何新增現有實體之間的關聯性、如何移除關聯性,以及如何新增具有現有實體關聯性的新實體。