Entity Framework 4.0 Database First と ASP.NET 4 Web Forms の概要 - 第 2 部
著者: Tom Dykstra
Contoso University のサンプル Web アプリケーションでは、Entity Framework 4.0 と Visual Studio 2010 を使用して ASP.NET Web Forms アプリケーションを作成する方法を示します。 チュートリアル シリーズについては、シリーズの最初のチュートリアルをご覧ください
EntityDataSource コントロール
前のチュートリアルでは、Web サイト、データベース、データ モデルを作成しました。 このチュートリアルでは、Entity Framework データ モデルの操作を容易にするために ASP.NET が提供する EntityDataSource
コントロールを操作します。 学生データを表示および編集するための GridView
コントロール、新しい学生を追加するための DetailsView
コントロール、および部門を選択するための DropDownList
コントロール (後で関連するコースを表示するために使用します) を作成します。
このアプリケーションでは、データベースを更新するページに入力検証を追加しないため、エラー処理の一部は運用アプリケーションで必要となる堅牢さはないことに注意してください そのため、チュートリアルは Entity Framework に焦点を絞り、長くなりすぎないようにしています。 これらの機能をアプリケーションに追加する方法の詳細については、「ASP.NET Web ページにおけるユーザー入力の検証」および「ASP.NET ページとアプリケーションでのエラー処理」を参照してください。
EntityDataSource コントロールの追加と構成
まず、People
エンティティ セットから Person
エンティティを読み取るように EntityDataSource
コントロールを構成します。
Visual Studio が開いていること、および第 1 部で作成したプロジェクトで作業していることを確認します。 データ モデルを作成してから、または最後に変更してからプロジェクトをビルドしていない場合は、ここでプロジェクトをビルドします。 データ モデルへの変更は、プロジェクトがビルドされるまでデザイナーで使用できません。
マスター ページ テンプレートを使用した Web フォームを使用して新しい Web ページを作成し、Students.aspx という名前を付けます。
マスター ページとして Site.Master を指定します。 これらのチュートリアルで作成するすべてのページで、このマスター ページが使用されます。
次の例に示すように、ソース ビューで、Content2
という名前の Content
コントロールに h2
見出しを追加します。
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Student List</h2>
</asp:Content>
[ツールボックス] の [データ] タブから、EntityDataSource
コントロールをページにドラッグし、見出しの下にドロップして、ID を StudentsEntityDataSource
に変更します。
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Student List</h2>
<asp:EntityDataSource ID="StudentsEntityDataSource" runat="server">
</asp:EntityDataSource>
</asp:Content>
デザイン ビューに切り替えて、データ ソース コントロールのスマート タグをクリックし、[データ ソースの構成] をクリックして、[データ ソースの構成] ウィザードを起動します。
[ObjectContext の構成] ウィザードのステップで、[名前付き接続] の値として [SchoolEntities] を選択し、[DefaultContainerName] の値として [SchoolEntities] を選択します。 続けて、 [次へ] をクリックします。
注: この時点で次のダイアログ ボックスが表示された場合は、続行する前にプロジェクトをビルドする必要があります。
[データの選択の構成] ステップで、[EntitySetName] の値として [People] を選択します。 [選択] の下で、[すべて選択] チェックボックスが選択されていることを確認します。 次に、更新と削除を有効にするオプションを選択します。 終了したら、[完了] をクリックします。
削除を許可するデータベース ルールの構成
他のテーブル (Course
、StudentGrade
、および OfficeAssignment
) と 3 つのリレーションシップを持つ Person
テーブルからユーザーが学生を削除できるようにするページを作成します。 既定では、他のテーブルのいずれかに関連する行がある場合、データベースで Person
内の行を削除できなくなります。 最初に関連する行を手動で削除することも、Person
行を削除するときに関連する行も自動的に削除するようにデータベースを構成することもできます。 このチュートリアルの学生レコードでは、関連データを自動的に削除するようにデータベースを構成します。 学生は StudentGrade
テーブル内にのみ関連する行を持つことができるため、3 つのリレーションシップのうち 1 つだけを構成する必要があります。
このチュートリアルに付属するプロジェクトからダウンロードした School.mdf ファイルを使用している場合は、これらの構成の変更が既に行われているため、このセクションをスキップできます。 スクリプトを実行してデータベースを作成した場合は、次の手順を実行してデータベースを構成します。
サーバー エクスプローラーで、第 1 部で作成したデータベース ダイアグラムを開きます。 Person
と StudentGrade
のリレーションシップ (テーブル間の線) を右クリックし、[プロパティ] を選択します。
[プロパティ] ウィンドウで、[INSERT および UPDATE の指定] を展開し、[DeleteRule] プロパティを [重ねて表示] に設定します。
ダイアグラムを保存して閉じます。 データベースを更新するかどうかを確認するメッセージが表示されたら、[はい] をクリックします。
モデルがメモリ内のエンティティをデータベースの処理と同期するようにするには、対応するルールをデータ モデルに設定する必要があります。 SchoolModel.edmx を開き、Person
と StudentGrade
間の関連行を右クリックして、[プロパティ] を選択します。
[プロパティ] ウィンドウで、[End1 OnDelete] を [重ねて表示] に設定します。
SchoolModel.edmx ファイルを保存して閉じ、プロジェクトをリビルドします。
一般に、データベースが変更された場合のモデルを同期する方法にはいくつかの選択肢があります。
- 特定の種類の変更 (テーブル、ビュー、ストアド プロシージャの追加や更新など) については、デザイナーを右クリックし、[データベースからモデルを更新] を選択して、デザイナーが自動的に変更を行うようにします。
- データ モデルを再生成します。
- このような手動更新を行います。
この場合は、モデルを再生成したり、リレーションシップの変更の影響を受けるテーブルを更新したりできますが、その後、フィールド名を再度変更する必要があります (FirstName
から FirstMidName
へ)。
GridView コントロールを使用したエンティティの読み取りと更新
このセクションでは、GridView
コントロールを使用して学生を表示、更新、または削除します。
Students.aspx を開くか切り替えて、デザイン ビューに切り替えます。 ツールボックスの [データ] タブから、GridView
コントロールを EntityDataSource
コントロールの右側にドラッグし、StudentsGridView
という名前を付けて、スマート タグをクリックし、データ ソースとして [StudentsEntityDataSource] を選択します。
[スキーマの更新] をクリックし (確認を求められた場合は [はい] をクリック)、[ページングを有効にする]、[並べ替えを有効にする]、[編集を有効にする]、および [削除を有効にする] をクリックします。
[列の編集] をクリックします。
[選択されたフィールド] ボックスで、[PersonID]、[LastName]、[HireDate] を削除します。 (通常、レコード キーはユーザーに対して表示せず、採用日は学生には関係ありません。また、名前の両方の部分を 1 つのフィールドに配置するため、必要な名前フィールドは 1 つだけです)。
FirstMidName フィールドを選択し、[このフィールドを TemplateField に変換します。] をクリックします。
EnrollmentDate に対しても同じ操作を行います。
[OK] をクリックして、ソース ビューに切り替えます。 残りの変更はマークアップで直接行う方が簡単です。 GridView
コントロールのマークアップは次の例のようになります。
<asp:GridView ID="StudentsGridView" runat="server" AllowPaging="True"
AllowSorting="True" AutoGenerateColumns="False" DataKeyNames="PersonID"
DataSourceID="StudentsEntityDataSource">
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:TemplateField HeaderText="FirstMidName" SortExpression="FirstMidName">
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("FirstMidName") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Bind("FirstMidName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="EnrollmentDate" SortExpression="EnrollmentDate">
<EditItemTemplate>
<asp:TextBox ID="TextBox2" runat="server" Text='<%# Bind("EnrollmentDate") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server" Text='<%# Bind("EnrollmentDate") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
コマンド フィールドの後の最初の列は、現状は名が表示されるテンプレート フィールドです。 このテンプレート フィールドのマークアップを次の例のように変更します。
<asp:TemplateField HeaderText="Name" SortExpression="LastName">
<EditItemTemplate>
<asp:TextBox ID="LastNameTextBox" runat="server" Text='<%# Bind("LastName") %>'></asp:TextBox>
<asp:TextBox ID="FirstNameTextBox" runat="server" Text='<%# Bind("FirstMidName") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="LastNameLabel" runat="server" Text='<%# Eval("LastName") %>'></asp:Label>,
<asp:Label ID="FirstNameLabel" runat="server" Text='<%# Eval("FirstMidName") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
表示モードでは、2 つの Label
コントロールに姓と名が表示されます。 編集モードでは、姓と名を変更できるように 2 つのテキスト ボックスが表示されます。 表示モードの Label
コントロールと同様に、データベースに直接接続する ASP.NET データ ソース コントロールとまったく同じように Bind
および Eval
式を使用します。 唯一の違いは、データベース列ではなくエンティティ プロパティを指定していることです。
最後の列は、登録日を表示するテンプレート フィールドです。 このフィールドのマークアップを次の例のように変更します。
<asp:TemplateField HeaderText="Enrollment Date" SortExpression="EnrollmentDate">
<EditItemTemplate>
<asp:TextBox ID="EnrollmentDateTextBox" runat="server" Text='<%# Bind("EnrollmentDate", "{0:d}") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="EnrollmentDateLabel" runat="server" Text='<%# Eval("EnrollmentDate", "{0:d}") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
表示モードと編集モードの両方で、書式設定文字列 "{0,d}" によって日付が "短い日付" 形式で表示されます。 (お使いのコンピューターは、このチュートリアルで示されている画面イメージとは異なる形式で表示するように設定されている可能性があります)。
これらの各テンプレート フィールドでは、デザイナーによって既定で Bind
式が使用されていますが、ItemTemplate
要素ではこれを Eval
式に変更していることに注意してください。 Bind
式を使用すると、コード内でデータにアクセスする必要がある場合に、GridView
コントロールのプロパティでデータを使用できるようになります。 このページでは、コード内でこのデータにアクセスする必要がないため、より効率的な Eval
を使用できます。 詳細については、「データ コントロールからデータを取得する」を参照してください。
パフォーマンスを向上させるための EntityDataSource コントロールのマークアップの修正
EntityDataSource
コントロールのマークアップで、ConnectionString
および DefaultContainerName
属性を削除し、それらを ContextTypeName="ContosoUniversity.DAL.SchoolEntities"
属性に置き換えます。 これは、オブジェクト コンテキスト クラスでハードコーディングされたものとは異なる接続を使用する必要がない限り、EntityDataSource
コントロールを作成するたびに行う必要がある変更です。 ContextTypeName
属性を使用することには次のような利点があります。
- パフォーマンスが向上します。
EntityDataSource
コントロールは、ConnectionString
およびDefaultContainerName
属性を使用してデータ モデルを初期化するときに、すべての要求でメタデータを読み込むための追加作業を実行します。ContextTypeName
属性を指定する場合、これは必要ありません。 - Entity Framework 4.0 で生成されたオブジェクト コンテキスト クラス (このチュートリアルの
SchoolEntities
など) では、遅延読み込みが既定で有効になっています。 つまり、ナビゲーション プロパティには、必要なときに関連データが自動的に読み込まれます。 遅延読み込みについては、このチュートリアルの後半で詳しく説明します。 - オブジェクト コンテキスト クラス (この場合は、
SchoolEntities
クラス) に適用したカスタマイズは、EntityDataSource
コントロールを使用するコントロールで使用できるようになります。 オブジェクト コンテキスト クラスのカスタマイズは高度なトピックであり、このチュートリアル シリーズの対象外です。 詳細については、「Entity Framework 生成型の拡張」を参照してください。
マークアップは次の例のようになります (プロパティの順序が異なる場合があります)。
<asp:EntityDataSource ID="StudentsEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EntitySetName="People"
EnableDelete="True" EnableUpdate="True">
</asp:EntityDataSource>
外部キー列がエンティティ プロパティとして公開されていないため、EnableFlattening
属性は以前のバージョンの Entity Framework において必要であった機能を参照します。 現在のバージョンでは、外部キーの関連付けの使用が可能になりました。つまり、多対多を除くすべての関連付けに対して外部キー プロパティが公開されます。 エンティティに外部キー プロパティがあり、複合型がない場合は、この属性を False
に設定したままにすることができます。 既定値は True
であるため、マークアップから属性を削除しないでください。 詳細については、「オブジェクトの平坦化 (EntityDataSource)」を参照してください。
ページを実行すると、学生と従業員の一覧が表示されます (次のチュートリアルでは、学生のみをフィルター処理します)。 名と姓が一緒に表示されます。
表示を並べ替えるには、列名をクリックします。
任意の行の [編集] をクリックします。 名と姓を変更できるテキスト ボックスが表示されます。
[削除] ボタンも機能します。 登録日を含む行の [削除] をクリックすると、その行は消えます。 (登録日のない行は講師を表しており、参照整合性エラーが発生する可能性があります。次のチュートリアルでは、この一覧をフィルター処理して学生のみを含めます)。
ナビゲーション プロパティからのデータの表示
ここで、各学生がいくつのコースを登録しているかを知りたいとします。 Entity Framework は、Person
エンティティの StudentGrades
ナビゲーション プロパティでその情報を提供します。 データベースの設計上、成績が割り当てられていない学生はコースに登録できないため、このチュートリアルでは、StudentGrade
テーブル行にコースに関連付けられた行があることが、コースに登録されていることと同じであると想定できます。 (Courses
ナビゲーション プロパティは講師専用です)。
EntityDataSource
コントロールの ContextTypeName
属性を使用すると、そのプロパティにアクセスしたときに、Entity Framework によってナビゲーション プロパティの情報が自動的に取得されます。 これは "遅延読み込み" と呼ばれます。 ただし、追加情報が必要になるたびにデータベースが個別に呼び出されるため、これは非効率的になる可能性があります。 EntityDataSource
コントロールによって返されるすべてのエンティティのナビゲーション プロパティからのデータが必要な場合は、データベースへの 1 回の呼び出しでエンティティ自体と共に関連データを取得する方が効率的です。 これは、"一括読み込み" と呼ばれ、EntityDataSource
コントロールの Include
プロパティを設定することによって、ナビゲーション プロパティの一括読み込みを指定します。
Students.aspx では、各学生のコース数を表示する必要があるため、一括読み込みが最適な選択です。 すべての学生を表示し、そのうちの一部の学生のコース数のみを表示する場合は (マークアップに加えてコードを記述する必要があります)、遅延読み込みの方が適切な選択である可能性があります。
Students.aspx を開くか切り替えて、デザイン ビューに切り替え、StudentsEntityDataSource
を選択し、[プロパティ] ウィンドウで [Include] プロパティを [StudentGrades] に設定します。 (複数のナビゲーション プロパティを取得する場合は、名前をコンマで区切って指定できます (例: StudentGrades, Courses))。
ソース ビューに切り替えます。 StudentsGridView
コントロールで、最後の asp:TemplateField
要素の後に、次の新しいテンプレート フィールドを追加します。
<asp:TemplateField HeaderText="Number of Courses">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Eval("StudentGrades.Count") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
Eval
式では、ナビゲーション プロパティ StudentGrades
を参照できます。 このプロパティにはコレクションが含まれているため、学生が登録しているコースの数を表示するために使用できる Count
プロパティがあります。 後のチュートリアルでは、コレクションではなく単一のエンティティを含むナビゲーション プロパティからのデータを表示する方法について説明します。 (ナビゲーション プロパティからのデータを表示するために BoundField
要素を使用することはできないことに注意してください)。
ページを実行すると、各学生が登録しているコースの数が表示されます。
DetailsView コントロールを使用したエンティティの挿入
次の手順では、新しい学生を追加できる DetailsView
コントロールを含むページを作成します。 ブラウザーを閉じ、Site.Master マスター ページを使用して新しい Web ページを作成します。 そのページに StudentsAdd.aspx という名前を付け、ソース ビューに切り替えます。
次のマークアップを追加して、Content2
という名前の Content
コントロールに対する既存のマークアップを置き換えます。
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Add New Students</h2>
<asp:EntityDataSource ID="StudentsEntityDataSource" runat="server"
ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="False"
EnableInsert="True" EntitySetName="People">
</asp:EntityDataSource>
<asp:DetailsView ID="StudentsDetailsView" runat="server"
DataSourceID="StudentsEntityDataSource" AutoGenerateRows="False"
DefaultMode="Insert">
<Fields>
<asp:BoundField DataField="FirstMidName" HeaderText="First Name"
SortExpression="FirstMidName" />
<asp:BoundField DataField="LastName" HeaderText="Last Name"
SortExpression="LastName" />
<asp:BoundField DataField="EnrollmentDate" HeaderText="Enrollment Date"
SortExpression="EnrollmentDate" />
<asp:CommandField ShowInsertButton="True" />
</Fields>
</asp:DetailsView>
</asp:Content>
このマークアップは、挿入を有効にする点を除き、Students.aspx で作成したコントロールと似た EntityDataSource
コントロールを作成します。 GridView
コントロールと同様に、DetailsView
コントロールのバインド フィールドは、エンティティ プロパティを参照することを除き、データベースに直接接続するデータ コントロールの場合とまったく同じようにコードを書きます。 この場合、DetailsView
コントロールは行の挿入にのみ使用されるため、既定のモードを Insert
に設定しています。
ページを実行して新しい学生を追加します。
新しい学生を挿入しても何も起こりませんが、ここで Students.aspx を実行すると、新しい学生の情報が表示されます。
ドロップダウン リストでのデータの表示
次の手順では、EntityDataSource
コントロールを使用して、DropDownList
コントロールをエンティティ セットにデータバインドします。 チュートリアルのこの部分では、このリストはあまり使用しません。 ただし、後続の部分では、このリストを使用して、ユーザーが学部を選択し、その部門に関連付けられているコースを表示できるようにします。
Courses.aspx という名前の新しい Web ページを作成します。 ソース ビューで、Content2
という名前の Content
コントロールに見出しを追加します。
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Courses by Department</h2>
</asp:Content>
デザイン ビューで、以前と同じように EntityDataSource
コントロールをページに追加します。ただし、今回は DepartmentsEntityDataSource
という名前を付けます。 [EntitySetName] 値として [部門] を選択し、[DepartmentID] および [Name] プロパティのみを選択します。
ツールボックスの [標準] タブから、DropDownList
コントロールをページにドラッグし、DepartmentsDropDownList
という名前を付けて、スマート タグをクリックし、[データ ソースの選択] を選択して、[DataSource 構成ウィザード] を起動します。
[データ ソースの選択] ステップで、データ ソースとして [DepartmentsEntityDataSource] を選択し、[スキーマの更新] をクリックして、表示するデータ フィールドとして [Name] を選択し、値のデータ フィールドとして [DepartmentID] を選択します。 OK をクリックします。
Entity Framework を使用してコントロールをデータバインドするために使用するメソッドは、エンティティとエンティティ プロパティを指定することを除いて、他の ASP.NET データ ソース コントロールと同じです。
ソース ビューに切り替えて、DropDownList
コントロールの直前に "部門を選択:" を追加します。
Select a department:
<asp:DropDownList ID="DropDownList1" runat="server"
DataSourceID="EntityDataSource1" DataTextField="Name"
DataValueField="DepartmentID">
</asp:DropDownList>
念のため、この時点で EntityDataSource
コントロールのマークアップを変更し、ConnectionString
および DefaultContainerName
属性を ContextTypeName="ContosoUniversity.DAL.SchoolEntities"
属性に置き換えます。 多くの場合、データ ソース コントロールにリンクされているデータバインド コントロールを作成してから、EntityDataSource
コントロールのマークアップを変更することをお勧めします。これは、変更を行った後、デザイナーによってデータバインド コントロールの [スキーマの更新] オプションが提供されないためです。
ページを実行すると、ドロップダウン リストから部門を選択できます。
これで、EntityDataSource
コントロールの使用方法の紹介は完了です。 このコントロールの操作は、テーブルと列の代わりにエンティティとプロパティを参照することを除いて、他の ASP.NET データ ソース コントロールの操作と基本的に変わりません。 唯一の例外は、ナビゲーション プロパティにアクセスする場合です。 次のチュートリアルでは、データのフィルター処理、グループ化、並べ替え時に、EntityDataSource
コントロールで使用する構文が他のデータ ソース コントロールと異なる場合があることを確認します。