チュートリアル : ストアド プロシージャへのエンティティのマッピング
このトピックでは、ADO.NET Entity Data Model デザイナ (エンティティ デザイナ) を使用してエンティティ型の挿入、更新、および削除の操作をストアド プロシージャにマップする方法について説明します。エンティティ型の挿入、更新、および削除の操作では、システムによって自動的に生成される SQL ステートメントを使用することも (既定の方法)、特定のストアド プロシージャが使用されるように指定することもできます。エンティティの作成、更新、および削除に使用されるアプリケーション コードは、データベースの更新にストアド プロシージャを使用するかどうかにかかわらず、同じです。
このチュートリアルでは、CourseManager アプリケーションで使用する Entity Data Model (EDM) を変更することによって、2 つのエンティティ型をストアド プロシージャにマップします (詳細は、このトピックの「前提条件」を参照してください)。エンティティ型の挿入、更新、および削除のためのコードも記述します。
前提条件
このチュートリアルを完了するには、CourseManager アプリケーションを構築する必要があります。詳細情報と手順については、「Entity Framework クイック スタート」を参照してください。このアプリケーションを構築した後に、2 つのエンティティ型をストアド プロシージャにマップして EDM を変更します。
[!メモ]
このドキュメントのチュートリアルのトピックの多くは CourseManager アプリケーションを開始点としているため、元の CourseManager コードを編集するのではなく、このチュートリアル用に CourseManager アプリケーションのコピーを使用することをお勧めします。
このチュートリアルでは、Visual Studio、.NET Framework、および Visual C# または Visual Basic のプログラミングの基本的なスキルがある読者を想定しています。
Person エンティティのストアド プロシージャへのマッピング
エンティティの挿入操作をストアド プロシージャにマップする際に、挿入される行の主キー値がサーバーで作成される場合は、その値をエンティティのキー プロパティにマップする必要があります。この例では、新たに作成された主キーが InsertPerson ストアド プロシージャから結果セットの一部として返され、エンティティ デザイナの [<結果バインドの追加>] 機能を使用してエンティティ キー (PersonID) にマップされます。
Person エンティティをストアド プロシージャにマップするには
Visual Studio で CourseManager ソリューションを開きます。
ソリューション エクスプローラで、School.edmx ファイルをダブルクリックします。
ADO.NET Entity Data Model デザイナ (エンティティ デザイナ) で School.edmx ファイルが開きます。
Person エンティティ型を右クリックし、[ストアド プロシージャ マッピング] をクリックします。
[マッピングの詳細] ウィンドウにストアド プロシージャのマッピングが表示されます。
[<挿入関数の選択>] をクリックします。
フィールドが、EDM に含まれているストアド プロシージャのドロップダウン リストになります。
ドロップダウン リストから [InsertPerson] を選択します。
ストアド プロシージャのパラメータとエンティティのプロパティとの既定のマッピングが表示されます。矢印はマッピングの方向を表します (プロパティの値がストアド プロシージャのパラメータに渡される)。
[<結果バインドの追加>] をクリックします。
フィールドが編集可能になります。
"<結果バインドの追加>" を「NewPersonID」(InsertPerson ストアド プロシージャから返されるパラメータの名前) に置き換えます。Enter キーを押します。
既定では、NewPersonID はエンティティ キー PersonID にマップされます。矢印はマッピングの方向を表します (結果列の値がプロパティに渡される)。
[<更新関数の選択>] をクリックし、表示される一覧で [UpdatePerson] をクリックします。
ストアド プロシージャのパラメータとエンティティのプロパティとの既定のマッピングが表示されます。
[<削除関数の選択>] をクリックし、表示される一覧で [DeletePerson] をクリックします。
ストアド プロシージャのパラメータとエンティティのプロパティとの既定のマッピングが表示されます。
以上で、Person エンティティ型の挿入、更新、および削除の操作がストアド プロシージャにマップされました。
OfficeAssignment エンティティのストアド プロシージャへのマッピング
一対一のアソシエーションの一方の End のエンティティ型がストアド プロシージャにマップされている場合、もう一方の End のエンティティもストアド プロシージャにマップする必要があります。この例では、OfficeAssignment エンティティ型に Person エンティティ型との一対一のアソシエーションがあるため、このエンティティ型もストアド プロシージャにマップします。このマッピングでは、アプリケーションのコードで同時実行を簡単にチェックできるように、更新操作で [元の値を使用する] オプションを使用します。
OfficeAssignment エンティティをストアド プロシージャにマップするには
OfficeAssignment エンティティ型を右クリックし、[Stored Procedure Mapping] をクリックします。
[マッピングの詳細] ウィンドウにストアド プロシージャのマッピングが表示されます。
[<挿入関数の選択>] をクリックし、表示される一覧で [InsertOfficeAssignment] をクリックします。
ストアド プロシージャのパラメータとエンティティのプロパティとの既定のマッピングが表示されます。
[<結果バインドの追加>] をクリックします。
フィールドが編集可能になります。
"<結果バインドの追加>" の代わりに「Timestamp」と入力します。
[Propery/Value] 列で、Timestamp の横にある空のフィールドをクリックします。
フィールドが、InsertOfficeAssignment ストアド プロシージャから返される値をマップできるプロパティのドロップダウン リストになります。
ドロップダウン リストから [Timestamp] を選択します。
[<更新関数の選択>] をクリックし、表示される一覧で [UpdateOfficeAssignment] をクリックします。
ストアド プロシージャのパラメータとエンティティのプロパティとの既定のマッピングが表示されます。[元の値を使用する] 列のマップされる各プロパティの横にチェック ボックスが表示されます。
[プロパティ] 列で、OrigTimestamp パラメータに対応する空のフィールドをクリックし、表示される一覧で [Timestamp] をクリックします。
このマッピングは、パラメータ名がプロパティ名に正確に一致していないため、エンティティ デザイナで既定のマッピングとして処理されません。
[元の値を使用する] 列で、Timestamp プロパティに対応するチェック ボックスをオンにします。
更新の際、データがデータベースに書き込まれるときに、データベースから読み取られた元の Timestamp プロパティの値が使用されます。その値がデータベースの値と一致しない場合は、OptimisticConcurrencyException がスローされます。
[<結果バインドの追加>] をクリックします。
フィールドが編集可能になります。
"<結果バインドの追加>" を「Timestamp」に置き換えます。
[Propery/Value] 列で、Timestamp の横にある空のフィールドをクリックします。
フィールドが、UpdateOfficeAssignment ストアド プロシージャから返される結果列をマップできるプロパティのドロップダウン リストになります。
ドロップダウン リストから [Timestamp] を選択します。
[<削除関数の選択>] をクリックし、表示される一覧で [DeleteOfficeAssignment] をクリックします。
ストアド プロシージャのパラメータとエンティティのプロパティとの既定のマッピングが表示されます。
以上で、OfficeAssignment エンティティ型の挿入、更新、および削除の操作がストアド プロシージャにマップされました。
ユーザー インターフェイスの構築
次に、CourseManager アプリケーションに 2 つのフォームを追加します。インストラクタの情報を表示したり更新したりするためのインターフェイスを提供するフォームと、オフィスの割り当てを表示したり更新したりするためのインターフェイスを提供するフォームです。
ユーザー インターフェイスを構築するには
ソリューション エクスプローラで [CourseManager] プロジェクトを右クリックし、[追加] をポイントして [新しい項目] をクリックします。
[新しい項目の追加] ダイアログ ボックスが表示されます。
[Windows フォーム] を選択し、フォーム名を InstructorViewer.vb または InstructorViewer.cs に設定して、[追加] をクリックします。
新しいフォームがプロジェクトに追加され、フォーム デザイナで開かれます。フォーム名が InstructorViewer に、テキストが InstructorViewer に設定されます。
DataGridView コントロールをツールボックスからフォームにドラッグし、[プロパティ] ウィンドウで [名前] を instructorGridView に設定します。
Button コントロールをツールボックスからフォームにドラッグします。[名前] を updateInstructor に、[テキスト] を Update Instructor に設定します。
Button コントロールをツールボックスからフォームにもう 1 つドラッグします。[名前] を viewOffices に、[テキスト] を View Offices に設定します。
ソリューション エクスプローラで [CourseManager] プロジェクトを右クリックし、[追加] をポイントして [新しい項目] をクリックします。
[新しい項目の追加] ダイアログ ボックスが表示されます。
[Windows フォーム] を選択し、フォーム名を OfficeViewer.vb または OfficeViewer.cs に設定して、[追加] をクリックします。
新しいフォームがプロジェクトに追加され、フォーム デザイナで開かれます。フォーム名が OfficeViewer に、テキストが OfficeViewer に設定されます。
ComboBox コントロールをツールボックスからフォームにドラッグし、[名前] を instructorList に設定します。
TextBox コントロールをツールボックスからフォームにドラッグし、[名前] を officeLocation に設定します。
Button コントロールをツールボックスからフォームにドラッグします。[名前] を updateOffice に、[テキスト] を Update Office に設定します。
ソリューション エクスプローラで、CourseViewer.vb または CourseViewer.cs をダブルクリックします。
CourseViewer フォームのデザイン ビューが表示されます。
Button コントロールをツールボックスからフォームにドラッグします。
[プロパティ] ウィンドウで、Button の [名前] プロパティを viewInstructors に、[テキスト] プロパティを View Instructors に設定します。
viewInstructorsButton コントロールをダブルクリックします。
CourseViewer フォームの分離コード ファイルが開きます。
viewInstructors_Click イベント ハンドラに次のコードを追加します。
Dim instructorViewer As New InstructorViewer() instructorViewer.Visible = True
InstructorViewer instructorViewer = new InstructorViewer(); instructorViewer.Visible = true;
InstructorViewer フォームのデザイン ビューに戻ります。
viewOfficesButton コントロールをダブルクリックします。
Form2 の分離コード ファイルが開きます。
viewOffices_Click イベント ハンドラに次のコードを追加します。
Dim officeViewer As New OfficeViewer() officeViewer.Visible = True
OfficeViewer officeViewer = new OfficeViewer(); officeViewer.Visible = true;
ユーザー インターフェイスが完成しました。
インストラクタの情報の表示と更新
この手順では、InstructorViewer フォームにコードを追加して、インストラクタの情報を表示したり更新したりできるようにします。具体的には、次の処理を行うコードを追加します。
Person 型 (インストラクタ) に関する情報を返すクエリに DataGridView をバインドする。オブジェクトをコントロールにバインドする方法の詳細については、「コントロールへのオブジェクトのバインド (Entity Framework)」を参照してください。
DataGridView コントロールの変更 (挿入、更新、または削除) をデータベースに保存する。
updateInstructor_Click イベント ハンドラで SaveChanges() が呼び出されたら、先ほどマップしたストアド プロシージャを使用してデータをデータベースに書き込む。
インストラクタの情報を表示および更新するには
フォーム デザイナで InstructorViewer フォームが開いた状態で、InstructorViewer フォームをダブルクリックします。
InstructorViewer フォームの分離コード ファイルが開きます。
次の using (C#) ステートメントまたは Imports (Visual Basic) ステートメントを追加します。
Imports System.Data.Objects Imports System.Data.Objects.DataClasses
using System.Data.Objects; using System.Data.Objects.DataClasses;
オブジェクト コンテキストを表すプロパティを InstructorViewer クラスに追加します。
' Create an ObjectContext instance based on SchoolEntity. Private schoolContext As SchoolEntities
// Create an ObjectContext instance based on SchoolEntity. private SchoolEntities schoolContext;
InstructorViewer_Load イベント ハンドラに、オブジェクト コンテキストを初期化し、DataGridView コントロールのデータ ソースを、HireDate が null ではないすべての Person 型を返すクエリに設定するコードを追加します。
' Initialize the ObjectContext. schoolContext = New SchoolEntities() Dim instructorQuery As ObjectQuery(Of Person) = _ schoolContext.Person.Include("OfficeAssignment") _ .Where("it.HireDate is not null") _ .OrderBy("it.LastName") instructorGridView.DataSource = instructorQuery _ .Execute(MergeOption.OverwriteChanges) instructorGridView.Columns("EnrollmentDate").Visible = False
// Initialize schoolContext. schoolContext = new SchoolEntities(); // Define the query to retrieve instructors. ObjectQuery<Person> instructorQuery = schoolContext.Person .Include("OfficeAssignment") .Where("it.HireDate is not null") .OrderBy("it.LastName"); // Execute and bind the instructorList control to the query. instructorGridView.DataSource = instructorQuery. Execute(MergeOption.OverwriteChanges); instructorGridView.Columns["EnrollmentDate"].Visible = false;
InstructorViewer フォームのデザイン ビューに戻り、updateInstructorButton コントロールをダブルクリックします。
updateInstructor_Click イベント ハンドラが分離コード ファイルに追加されます。
updateInstructor_Click イベント ハンドラに、instructorGridViewDataGridView コントロールでインストラクタの情報に加えられた変更を保存するコードを追加します。
Dim numChanges As Integer ' Save object changes to the database, display a ' message, and refresh the form. numChanges = schoolContext.SaveChanges() MessageBox.Show(numChanges.ToString() + _ " change(s) saved to the database.") Me.Refresh()
int numChanges; // Save object changes to the database, display a // message, and refresh the form. numChanges = schoolContext.SaveChanges(); MessageBox.Show(numChanges.ToString() + " change(s) saved to the database."); this.Refresh();
Ctrl キーを押しながら F5 キーを押してアプリケーションを実行します。インストラクタの情報を表示したり更新したりできます。そのためには、[View Instructors] をクリックし、表示されるテーブルで変更を加えて、[Update Instructor] をクリックします。
オフィスの情報の表示と更新
この手順では、OfficeViewer フォームにコードを追加して、オフィスの割り当ての情報を表示したり更新したりできるようにします。具体的には、次の処理を行うコードを追加します。
インストラクタの情報を返すクエリに ComboBox をバインドする。
選択されたインストラクタのオフィスの場所の情報を TextBox に表示する。
updateOffice_Click イベント ハンドラで SaveChanges() が呼び出されたら、先ほどマップしたストアド プロシージャを使用してデータをデータベースに書き込む。
オフィスの情報を表示および更新するには
フォーム デザイナで OfficeViewer フォームが開いた状態で、OfficeViewer フォームをダブルクリックします。
OfficeViewer フォームの分離コード ファイルが開きます。
次の using (C#) ステートメントまたは Imports (Visual Basic) ステートメントを追加します。
Imports System.Data.Objects Imports System.Data.Objects.DataClasses
using System.Data.Objects; using System.Data.Objects.DataClasses;
オブジェクト コンテキストを表すプロパティを OfficeViewer クラスに追加します。
' Create an ObjectContext instance based on SchoolEntity. Private schoolContext As SchoolEntities
// Create an ObjectContext instance based on SchoolEntity. private SchoolEntities schoolContext;
フォームに次のメソッドを追加します。
Private Sub ExecuteInstructorQuery() ' Define the query to retrieve instructors. Dim instructorQuery As ObjectQuery(Of Person) = _ schoolContext.Person.Include("OfficeAssignment"). _ Where("it.HireDate is not null").OrderBy("it.LastName") 'Execute and bind the instructorList control to the query. 'Using MergeOption.OverwriteChanges overwrites local data 'with data from the database. instructorList.DataSource = instructorQuery _ .Execute(MergeOption.OverwriteChanges) instructorList.DisplayMember = "LastName" End Sub
private void ExecuteInstructorQuery() { // Define the query to retrieve instructors. ObjectQuery<Person> instructorQuery = schoolContext.Person .Include("OfficeAssignment") .Where("it.HireDate is not null") .OrderBy("it.LastName"); //Execute and bind the instructorList control to the query. //Using MergeOption.OverwriteChanges overwrites local data //with data from the database. instructorList.DataSource = instructorQuery .Execute(MergeOption.OverwriteChanges); instructorList.DisplayMember = "LastName"; }
このメソッドは、インストラクタの情報を返すクエリを実行し、結果を instructorListComboBox コントロールにバインドします。
OfficeViewer_Load イベント ハンドラに、オブジェクト コンテキストを初期化し、HireDate が null ではないすべての Person 型を返すクエリに ComboBox コントロールをバインドするメソッドを呼び出すコードを追加します。
schoolContext = New SchoolEntities() ExecuteInstructorQuery()
schoolContext = new SchoolEntities(); ExecuteInstructorQuery();
OfficeViewer フォームのデザイン ビューに戻り、instructorListComboBox コントロールをダブルクリックします。
instructorList_SelectedIndexChanged イベント ハンドラが分離コード ファイルに追加されます。
イベント ハンドラに、選択されたインストラクタのオフィスの場所を ListBox コントロールに表示し、updateOfficeButton コントロールを無効にするコードを追加します。このコントロールは、選択されたオフィスの場所が変更されると有効になります。
Dim instructor As Person = CType(Me.instructorList _ .SelectedItem(), Person) If Not instructor.OfficeAssignment Is Nothing Then Me.officeLocation.Text = instructor _ .OfficeAssignment.Location.ToString() Else Me.officeLocation.Text = "" End If ' Disable the updateOffice button until a change ' has been made to the office location. updateOffice.Enabled = False 'forceChanges.Enabled = False
Person instructor = (Person)this.instructorList. SelectedItem; if (instructor.OfficeAssignment != null) { this.officeLocation.Text = instructor. OfficeAssignment.Location.ToString(); } else { this.officeLocation.Text = ""; } // Disable the updateOffice button until a change // has been made to the office location. updateOffice.Enabled = false; //forceChanges.Enabled = false;
OfficeViewer フォームのデザイン ビューに戻り、updateOfficeButton コントロールをダブルクリックします。
updateOffice_Click イベント ハンドラが分離コード ファイルに追加されます。
officeLocationTextBox コントロールでオフィスの情報に加えられた変更を保存するコードを追加します。
Try Dim numChanges As Integer Dim currentInstructor As Person = CType(Me.instructorList _ .SelectedItem(), Person) If Me.officeLocation.Text <> String.Empty Then If Not currentInstructor.OfficeAssignment Is Nothing Then currentInstructor.OfficeAssignment.Location() = _ Me.officeLocation.Text Else Dim temp(8) As Byte currentInstructor.OfficeAssignment = _ OfficeAssignment.CreateOfficeAssignment( _ currentInstructor.PersonID, _ Me.officeLocation.Text, temp) End If Else schoolContext.DeleteObject(currentInstructor. _ OfficeAssignment) End If numChanges = schoolContext.SaveChanges() MessageBox.Show(numChanges.ToString() _ + " change(s) saved to the database.") Catch oce As OptimisticConcurrencyException MessageBox.Show(oce.Message + " The conflict " & _ "occurred on " & oce.StateEntries(0).Entity _ .ToString() & "with key value " & _ oce.StateEntries(0).EntityKey.EntityKeyValues(0) _ .Value) 'forceChanges.Enabled = True Catch ue As UpdateException MessageBox.Show(ue.Message & " Click OK to retrieve " _ & "the latest data from the database.") ExecuteInstructorQuery() Me.Refresh() Finally ' Disable the updateOffice button until another ' change has been made to the location. updateOffice.Enabled = False End Try
try { int numChanges; Person currentInstructor = (Person)this.instructorList. SelectedItem; if (this.officeLocation.Text != string.Empty) { if (currentInstructor.OfficeAssignment != null) { currentInstructor.OfficeAssignment.Location = this.officeLocation.Text; } else { currentInstructor.OfficeAssignment = OfficeAssignment.CreateOfficeAssignment( currentInstructor.PersonID, this.officeLocation.Text, new byte[8]); } } else { schoolContext.DeleteObject(currentInstructor .OfficeAssignment); } numChanges = schoolContext.SaveChanges(); MessageBox.Show(numChanges.ToString() + " change(s) saved to the database."); } catch (OptimisticConcurrencyException oce) { MessageBox.Show(oce.Message + " The conflict " + "occurred on " + oce.StateEntries[0].Entity + " with key value " + oce.StateEntries[0]. EntityKey.EntityKeyValues[0].Value); //forceChanges.Enabled = true; } catch (UpdateException ue) { MessageBox.Show(ue.Message + " Click OK to retrieve " + "the latest data from the database."); ExecuteInstructorQuery(); this.Refresh(); } finally { // Disable the updateOffice button until another // change has been made to the location. updateOffice.Enabled = false; }
OfficeViewer フォームのデザイン ビューに戻り、officeLocationTextBox コントロールをダブルクリックします。
officeLocation_TextChanged イベント ハンドラが分離コード ファイルに追加されます。
選択されたオフィスの場所が変更された場合に updateOfficeButton コントロールを有効にするコードを追加します。
' Enable the udateOffice button when there is a change ' to write to the database. updateOffice.Enabled = True
// Enable the udateOffice button when there is a change // to write to the database. updateOffice.Enabled = true;
アプリケーションが完成しました。Ctrl キーを押しながら F5 キーを押してアプリケーションを実行します。OfficeViewer フォームでオフィスの情報を表示したり更新したりできます。
同時実行の競合の処理
この手順では、同時実行の競合が発生した場合にクライアントの変更を強制的にデータベースに適用するコードを Office Viewer フォームに追加します。
同時実行の競合を処理するには
ソリューション エクスプローラで、InstructorViewer.vb または InstructorViewer.cs をダブルクリックします。
フォーム デザイナでフォームが開きます。
[View Offices] ボタンをダブルクリックします。
InstructorViewer フォームの分離コード ファイルが開きます。
viewOffices_Click イベント ハンドラに次のコードを追加して、[View Offices] ボタンをクリックすると 2 つの OfficeViewer フォームが読み込まれるようにします。
Dim officeViewer2 As New OfficeViewer() officeViewer2.Text = "Demonstrate Conflict" officeViewer2.Visible = True
OfficeViewer officeViewer2 = new OfficeViewer(); officeViewer2.Text = "Demonstrate Conflict"; officeViewer2.Visible = true;
ソリューション エクスプローラで、OfficeViewer.vb または OfficeViewer.cs をダブルクリックします。
フォーム デザイナでフォームが開きます。
Button コントロールをツールボックスからフォームにドラッグします。[名前] を forceChanges に、[テキスト] を Force Changes に設定します。
[Force Changes] ボタンをダブルクリックします。
Office Viewer フォームの分離コード ファイルが開きます。
forceChanges_Click イベント ハンドラに次のコードを追加して、クライアントの変更が強制的にサーバーに適用されるか、instructorListComboBox コントロールにバインドされているデータがデータベースから更新されるようにします。
Dim numChanges As Integer Dim currentInstructor As Person = CType(Me.instructorList _ .SelectedItem(), Person) Try currentInstructor.OfficeAssignment.Location = _ Me.officeLocation.Text ' Using RefreshMode.ClientWins disables the ' optimistic concurrency check. schoolContext.Refresh(RefreshMode.ClientWins, _ currentInstructor.OfficeAssignment) numChanges = schoolContext.SaveChanges() MessageBox.Show(numChanges.ToString() + _ " change(s) saved to the database.") 'forceChanges.Enabled = False Catch ioe As InvalidOperationException MessageBox.Show(ioe.Message + " Click OK to retrieve " _ + "the latest data from the database.") ExecuteInstructorQuery() Me.Refresh() End Try
int numChanges; Person currentInstructor = (Person)this.instructorList .SelectedItem; try { currentInstructor.OfficeAssignment.Location = this.officeLocation.Text; // Using RefreshMode.ClientWins disables the // optimistic concurrency check. schoolContext.Refresh(RefreshMode.ClientWins, currentInstructor.OfficeAssignment); numChanges = schoolContext.SaveChanges(); MessageBox.Show(numChanges.ToString() + " change(s) saved to the database."); //forceChanges.Enabled = false; } catch (InvalidOperationException ioe) { MessageBox.Show(ioe.Message + " Click OK to retrieve " + "the latest data from the database."); ExecuteInstructorQuery(); this.Refresh(); }
instructorList_SelectedIndexChanged イベント ハンドラのコードの
forceChanges = False
(Visual Basic) またはforceChanges = false;
(C#) という行のコメントを解除して、新しいインストラクタが選択されると [Force Changes] ボタンが無効になるようにします。updateOffice_Click イベント ハンドラのコードの
forceChanges = True
(Visual Basic) またはforceChanges = true;
(C#) という行のコメントを解除して、同時実行の競合が発生すると [Force Changes] ボタンが有効になるようにします。forceChanges_Click イベント ハンドラのコードの
forceChanges = False
(Visual Basic) またはforceChanges = false;
(C#) という行のコメントを解除して、変更が強制的にデータベースに適用された後に [Force Changes] ボタンが無効になるようにします。
アプリケーションの同時実行の競合の処理を確認するには、まずアプリケーションを実行し (Ctrl キーを押しながら F5 キーを押します)、[View Instructors] をクリックして、[View Offices] をクリックします。次に、[Office Viewer] フォームでオフィスの場所を更新します。その後、もう 1 つのフォーム ([Demonstrate Conflict] フォーム) で同じオフィスの場所を更新しようとすると、同時実行の競合を通知するメッセージ ボックスが表示されます。[Demonstrate Conflict] フォームの変更を強制的にデータベースに適用するには、[Force Changes] をクリックします。
コード リスト
このセクションでは、InstructorViewer フォームと OfficeViewer フォームの分離コード ファイルの最終バージョンを示します。
Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Public Class InstructorViewer
' Create an ObjectContext instance based on SchoolEntity.
Private schoolContext As SchoolEntities
Private Sub viewOffices_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles viewOffices.Click
Dim officeViewer As New OfficeViewer()
officeViewer.Visible = True
Dim officeViewer2 As New OfficeViewer()
officeViewer2.Text = "Demonstrate Conflict"
officeViewer2.Visible = True
End Sub
Private Sub InstructorViewer_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
' Initialize the ObjectContext.
schoolContext = New SchoolEntities()
Dim instructorQuery As ObjectQuery(Of Person) = _
schoolContext.Person.Include("OfficeAssignment") _
.Where("it.HireDate is not null") _
.OrderBy("it.LastName")
instructorGridView.DataSource = instructorQuery _
.Execute(MergeOption.OverwriteChanges)
instructorGridView.Columns("EnrollmentDate").Visible = False
End Sub
Private Sub updateInstructor_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles updateInstructor.Click
Dim numChanges As Integer
' Save object changes to the database, display a
' message, and refresh the form.
numChanges = schoolContext.SaveChanges()
MessageBox.Show(numChanges.ToString() + _
" change(s) saved to the database.")
Me.Refresh()
End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace CourseManager
{
public partial class InstructorViewer : Form
{
// Create an ObjectContext instance based on SchoolEntity.
private SchoolEntities schoolContext;
public InstructorViewer()
{
InitializeComponent();
}
private void viewOffices_Click(object sender, EventArgs e)
{
OfficeViewer officeViewer = new OfficeViewer();
officeViewer.Visible = true;
OfficeViewer officeViewer2 = new OfficeViewer();
officeViewer2.Text = "Demonstrate Conflict";
officeViewer2.Visible = true;
}
private void InstructorViewer_Load(object sender, EventArgs e)
{
// Initialize schoolContext.
schoolContext = new SchoolEntities();
// Define the query to retrieve instructors.
ObjectQuery<Person> instructorQuery = schoolContext.Person
.Include("OfficeAssignment")
.Where("it.HireDate is not null")
.OrderBy("it.LastName");
// Execute and bind the instructorList control to the query.
instructorGridView.DataSource = instructorQuery.
Execute(MergeOption.OverwriteChanges);
instructorGridView.Columns["EnrollmentDate"].Visible = false;
}
private void updateInstructor_Click(object sender, EventArgs e)
{
int numChanges;
// Save object changes to the database, display a
// message, and refresh the form.
numChanges = schoolContext.SaveChanges();
MessageBox.Show(numChanges.ToString() +
" change(s) saved to the database.");
this.Refresh();
}
}
}
Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Public Class OfficeViewer
' Create an ObjectContext instance based on SchoolEntity.
Private schoolContext As SchoolEntities
Private Sub OfficeViewer_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
schoolContext = New SchoolEntities()
ExecuteInstructorQuery()
End Sub
Private Sub instructorList_SelectedIndexChanged(ByVal sender As _
System.Object, ByVal e As System.EventArgs) Handles _
instructorList.SelectedIndexChanged
Dim instructor As Person = CType(Me.instructorList _
.SelectedItem(), Person)
If Not instructor.OfficeAssignment Is Nothing Then
Me.officeLocation.Text = instructor _
.OfficeAssignment.Location.ToString()
Else
Me.officeLocation.Text = ""
End If
' Disable the updateOffice button until a change
' has been made to the office location.
updateOffice.Enabled = False
'forceChanges.Enabled = False
End Sub
Private Sub updateOffice_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles updateOffice.Click
Try
Dim numChanges As Integer
Dim currentInstructor As Person = CType(Me.instructorList _
.SelectedItem(), Person)
If Me.officeLocation.Text <> String.Empty Then
If Not currentInstructor.OfficeAssignment Is Nothing Then
currentInstructor.OfficeAssignment.Location() = _
Me.officeLocation.Text
Else
Dim temp(8) As Byte
currentInstructor.OfficeAssignment = _
OfficeAssignment.CreateOfficeAssignment( _
currentInstructor.PersonID, _
Me.officeLocation.Text, temp)
End If
Else
schoolContext.DeleteObject(currentInstructor. _
OfficeAssignment)
End If
numChanges = schoolContext.SaveChanges()
MessageBox.Show(numChanges.ToString() _
+ " change(s) saved to the database.")
Catch oce As OptimisticConcurrencyException
MessageBox.Show(oce.Message + " The conflict " & _
"occurred on " & oce.StateEntries(0).Entity _
.ToString() & "with key value " & _
oce.StateEntries(0).EntityKey.EntityKeyValues(0) _
.Value)
'forceChanges.Enabled = True
Catch ue As UpdateException
MessageBox.Show(ue.Message & " Click OK to retrieve " _
& "the latest data from the database.")
ExecuteInstructorQuery()
Me.Refresh()
Finally
' Disable the updateOffice button until another
' change has been made to the location.
updateOffice.Enabled = False
End Try
End Sub
Private Sub officeLocation_TextChanged(ByVal sender As _
System.Object, ByVal e As System.EventArgs) _
Handles officeLocation.TextChanged
' Enable the udateOffice button when there is a change
' to write to the database.
updateOffice.Enabled = True
End Sub
Private Sub forceChanges_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles forceChanges.Click
Dim numChanges As Integer
Dim currentInstructor As Person = CType(Me.instructorList _
.SelectedItem(), Person)
Try
currentInstructor.OfficeAssignment.Location = _
Me.officeLocation.Text
' Using RefreshMode.ClientWins disables the
' optimistic concurrency check.
schoolContext.Refresh(RefreshMode.ClientWins, _
currentInstructor.OfficeAssignment)
numChanges = schoolContext.SaveChanges()
MessageBox.Show(numChanges.ToString() + _
" change(s) saved to the database.")
'forceChanges.Enabled = False
Catch ioe As InvalidOperationException
MessageBox.Show(ioe.Message + " Click OK to retrieve " _
+ "the latest data from the database.")
ExecuteInstructorQuery()
Me.Refresh()
End Try
End Sub
Private Sub ExecuteInstructorQuery()
' Define the query to retrieve instructors.
Dim instructorQuery As ObjectQuery(Of Person) = _
schoolContext.Person.Include("OfficeAssignment"). _
Where("it.HireDate is not null").OrderBy("it.LastName")
'Execute and bind the instructorList control to the query.
'Using MergeOption.OverwriteChanges overwrites local data
'with data from the database.
instructorList.DataSource = instructorQuery _
.Execute(MergeOption.OverwriteChanges)
instructorList.DisplayMember = "LastName"
End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace CourseManager
{
public partial class OfficeViewer : Form
{
// Create an ObjectContext instance based on SchoolEntity.
private SchoolEntities schoolContext;
public OfficeViewer()
{
InitializeComponent();
}
private void OfficeViewer_Load(object sender, EventArgs e)
{
schoolContext = new SchoolEntities();
ExecuteInstructorQuery();
}
private void instructorList_SelectedIndexChanged(object sender,
EventArgs e)
{
Person instructor = (Person)this.instructorList.
SelectedItem;
if (instructor.OfficeAssignment != null)
{
this.officeLocation.Text = instructor.
OfficeAssignment.Location.ToString();
}
else
{
this.officeLocation.Text = "";
}
// Disable the updateOffice button until a change
// has been made to the office location.
updateOffice.Enabled = false;
//forceChanges.Enabled = false;
}
private void updateOffice_Click(object sender, EventArgs e)
{
try
{
int numChanges;
Person currentInstructor = (Person)this.instructorList.
SelectedItem;
if (this.officeLocation.Text != string.Empty)
{
if (currentInstructor.OfficeAssignment != null)
{
currentInstructor.OfficeAssignment.Location
= this.officeLocation.Text;
}
else
{
currentInstructor.OfficeAssignment
= OfficeAssignment.CreateOfficeAssignment(
currentInstructor.PersonID, this.officeLocation.Text,
new byte[8]);
}
}
else
{
schoolContext.DeleteObject(currentInstructor
.OfficeAssignment);
}
numChanges = schoolContext.SaveChanges();
MessageBox.Show(numChanges.ToString() +
" change(s) saved to the database.");
}
catch (OptimisticConcurrencyException oce)
{
MessageBox.Show(oce.Message + " The conflict "
+ "occurred on " + oce.StateEntries[0].Entity
+ " with key value " + oce.StateEntries[0].
EntityKey.EntityKeyValues[0].Value);
//forceChanges.Enabled = true;
}
catch (UpdateException ue)
{
MessageBox.Show(ue.Message + " Click OK to retrieve "
+ "the latest data from the database.");
ExecuteInstructorQuery();
this.Refresh();
}
finally
{
// Disable the updateOffice button until another
// change has been made to the location.
updateOffice.Enabled = false;
}
}
private void officeLocation_TextChanged(object sender, EventArgs e)
{
// Enable the udateOffice button when there is a change
// to write to the database.
updateOffice.Enabled = true;
}
private void forceChanges_Click(object sender, EventArgs e)
{
int numChanges;
Person currentInstructor = (Person)this.instructorList
.SelectedItem;
try
{
currentInstructor.OfficeAssignment.Location
= this.officeLocation.Text;
// Using RefreshMode.ClientWins disables the
// optimistic concurrency check.
schoolContext.Refresh(RefreshMode.ClientWins,
currentInstructor.OfficeAssignment);
numChanges = schoolContext.SaveChanges();
MessageBox.Show(numChanges.ToString() +
" change(s) saved to the database.");
//forceChanges.Enabled = false;
}
catch (InvalidOperationException ioe)
{
MessageBox.Show(ioe.Message + " Click OK to retrieve "
+ "the latest data from the database.");
ExecuteInstructorQuery();
this.Refresh();
}
}
private void ExecuteInstructorQuery()
{
// Define the query to retrieve instructors.
ObjectQuery<Person> instructorQuery = schoolContext.Person
.Include("OfficeAssignment")
.Where("it.HireDate is not null")
.OrderBy("it.LastName");
//Execute and bind the instructorList control to the query.
//Using MergeOption.OverwriteChanges overwrites local data
//with data from the database.
instructorList.DataSource = instructorQuery
.Execute(MergeOption.OverwriteChanges);
instructorList.DisplayMember = "LastName";
}
}
}
次の手順
ここでは、エンティティの挿入、更新、および削除の操作をストアド プロシージャにマップしました。Entity Framework でのストアド プロシージャのサポートの詳細については、「ストアド プロシージャのサポート (Entity Framework)」を参照してください。Entity Framework を使用するアプリケーションを構築する方法の詳細については、「プログラミング ガイド (Entity Framework)」を参照してください。
参照
その他のリソース
ADO.NET Entity Data Model デザイナのシナリオ
Entity Data Model ツールのタスク