Entity Framework 4.0 Database First と ASP.NET 4 Web Formsを使用したはじめに - パート 4
Contoso University サンプル Web アプリケーションは、Entity Framework 4.0 と Visual Studio 2010 を使用して ASP.NET Web Forms アプリケーションを作成する方法を示しています。 チュートリアル シリーズの詳細については、シリーズの最初のチュートリアルを参照してください
関連データの操作
前のチュートリアルでは、 コントロールを使用してデータの EntityDataSource
フィルター処理、並べ替え、グループ化を行いました。 このチュートリアルでは、関連データを表示および更新します。
講師の一覧を表示する [講師] ページを作成します。 インストラクターを選択すると、そのインストラクターによって教えられたコースの一覧が表示されます。 コースを選択すると、コースの詳細と、コースに登録されている学生の一覧が表示されます。 講師名、採用日、およびオフィスの割り当てを編集できます。 Office の割り当ては、ナビゲーション プロパティを介してアクセスする個別のエンティティ セットです。
マスター データは、マークアップまたはコード内の詳細データにリンクできます。 チュートリアルのこの部分では、両方の方法を使用します。
GridView コントロールでの関連エンティティの表示と更新
Site.Master マスター ページを使用する Instructors.aspx という名前の新しい Web ページを作成し、 という名前Content2
のコントロールに次のマークアップをContent
追加します。
<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
間に、エラー メッセージに使用するコントロールとLabel
コントロールをGridView
作成する次のマークアップを追加</div>
します。
<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
コントロールは、行の選択を有効にし、選択した行を薄い灰色の背景色で強調表示し、 イベントと Updating
イベントのハンドラー (後で作成します) をSelectedIndexChanged
指定します。 また、選択した行のキー値を後で追加する別のコントロールに渡すことができるように、 プロパティにも を指定PersonID
DataKeyNames
します。
最後の列には講師のオフィスの割り当てが含まれています。これは、関連付けられたエンティティから取得されるため、エンティティの Person
ナビゲーション プロパティに格納されます。 コントロールをEditItemTemplate
更新するためにナビゲーション プロパティにGridView
直接バインドできないため、 要素は の代わりに Bind
を指定Eval
していることに注意してください。 コードで Office の割り当てを更新します。 これを行うには、コントロールへの参照がTextBox
必要です。コントロールのイベントにTextBox
Init
これを取得して保存します。
次の 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;
}
コントロールUpdating
の イベントをGridView
使用して、関連付けられているOfficeAssignment
エンティティの プロパティを更新Location
します。 イベントに次のハンドラーを 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
使用して、現在Person
のエンティティに関連付けられているエンティティを取得OfficeAssignment
します。
次に、コードはコントロールの値に応じて、次のいずれかのアクションを InstructorOfficeTextBox
実行します。
- テキスト ボックスに値があり、更新するエンティティがない
OfficeAssignment
場合は、その値が作成されます。 - テキスト ボックスに値があり、エンティティがある場合は
OfficeAssignment
、プロパティ値が更新されますLocation
。 - テキスト ボックスが空でエンティティが存在する
OfficeAssignment
場合は、エンティティが削除されます。
その後、変更がデータベースに保存されます。 例外が発生すると、エラー メッセージが表示されます。
ページを実行します。
[ 編集] をクリックすると、すべてのフィールドがテキスト ボックスに変わります。
Office の割り当てを含め、これらの値のいずれかを変更します。 [ 更新 ] をクリックすると、変更が一覧に反映されます。
関連エンティティを別のコントロールに表示する
各講師は 1 つ以上のコースを教えることができるので、コントロールとコントロールを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
、コントロールで行が PersonID
選択されている講師の の値が InstructorsGridView
含まれます。 プロパティにはWhere
、エンティティPeople
のナビゲーション プロパティからCourse
すべての関連付けられたエンティティを取得し、関連付けられているPerson
エンティティの 1 つにPerson
選択されたPersonID
値が含まれている場合にのみエンティティを選択Course
するサブセレクト コマンドが含まれています。
コントロールをGridView
作成するには、コントロールの直後 (終了</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
要素が含まれます。
ページを実行します。
1 つ以上のコースが割り当てられている講師を選択し、コースまたはコースが一覧に表示されます。 (注: データベース スキーマでは複数のコースが許可されますが、データベースで提供されるテスト データでは、インストラクターが実際に複数のコースを持つ必要はありません。[Server エクスプローラー] ウィンドウまたは CoursesAdd.aspx ページを使用して、データベースにコースを自分で追加できます。このページは、後のチュートリアルで追加します)。
コントロールには 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
、courses コントロールで選択した行の値を使用して CourseID
コースを GridView
選択します。 マークアップは、後で学生の成績を Selected
表示するために使用するイベントのハンドラーを指定します。これは、階層内のもう 1 つのレベル低いレベルです。
Instructors.aspx.cs で、 メソッドの次のスタブをCourseDetailsEntityDataSource_Selected
作成します。 (このスタブはチュートリアルの後半で入力します。ここでは、ページがコンパイルされて実行されるように、このスタブが必要です)。
protected void CourseDetailsEntityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
}
ページを実行します。
コースが選択されていないため、最初はコースの詳細はありません。 コースが割り当てられている講師を選択し、コースを選択して詳細を表示します。
EntityDataSource "Selected" イベントを使用して関連データを表示する
最後に、選択したコースに登録されているすべての学生とその成績を表示します。 これを行うには、コース DetailsView
にSelected
バインドされたコントロールの 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
表示する列を指定します。 学生 ID と学生の成績はエンティティからStudentGrade
取得され、学生名はエンティティのPerson
ナビゲーション プロパティStudentGrade
で Person
Entity Framework が使用できるエンティティからの名前です。
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();
}
}
このイベントのイベント引数は、選択したデータをコレクションの形式で提供します。何も選択されていない場合は項目が 0、エンティティが選択されている場合 Course
は 1 つの項目が含まれます。 エンティティが Course
選択されている場合、コードは メソッドを First
使用してコレクションを 1 つのオブジェクトに変換します。 その後、ナビゲーション プロパティからエンティティを取得 StudentGrade
し、それらをコレクションに変換し、コントロールを GradesListView
コレクションにバインドします。
これは成績を表示するのに十分ですが、空のデータ テンプレート内のメッセージが、ページが初めて表示されたとき、およびコースが選択されていないときに必ず表示されるようにする必要があります。 これを行うには、次のメソッドを作成します。このメソッドは、2 つの場所から呼び出します。
private void ClearStudentGradesDataSource()
{
var emptyStudentGradesList = new List<StudentGrade>();
GradesListView.DataSource = emptyStudentGradesList;
GradesListView.DataBind();
}
この新しいメソッドを Page_Load
メソッドから呼び出して、ページが初めて表示されるときに空のデータ テンプレートを表示します。 また、インストラクターが選択されるとイベントが発生するため、 メソッドから InstructorsGridView_SelectedIndexChanged
呼び出します。つまり、新しいコースがコース GridView
コントロールに読み込まれ、まだ選択されていません。 2 つの呼び出しを次に示します。
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ClearStudentGradesDataSource();
}
}
protected void InstructorsGridView_SelectedIndexChanged(object sender, EventArgs e)
{
ClearStudentGradesDataSource();
}
ページを実行します。
コースが割り当てられている講師を選択し、コースを選択します。
これで、関連データを操作するいくつかの方法を確認しました。 次のチュートリアルでは、既存のエンティティ間にリレーションシップを追加する方法、リレーションシップを削除する方法、既存のエンティティにリレーションシップを持つ新しいエンティティを追加する方法について説明します。
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示