チュートリアル: ストアド プロシージャへのエンティティのマッピング (Entity Data Model ツール)
このトピックでは、ADO.NET Entity Data Model デザイナー (エンティティ デザイナー) を使用してエンティティ型の挿入、更新、および削除の操作をストアド プロシージャにマップする方法について説明します。 エンティティ型の挿入、更新、および削除の操作では、システムによって自動的に生成される SQL ステートメントを使用することも (既定の方法)、特定のストアド プロシージャが使用されるように指定することもできます。 エンティティの作成、更新、および削除に使用されるアプリケーション コードは、データベースの更新にストアド プロシージャを使用するかどうかにかかわらず、同じです。
このチュートリアルでは、CourseManager アプリケーションで使用する .edmx ファイルを変更することによって、(概念モデルの) 2 つのエンティティ型を (ストレージ モデルの) ストアド プロシージャにマップします (詳細は、このトピックの「前提条件」を参照してください)。 エンティティ型の挿入、更新、および削除のためのコードも記述します。
注 : |
---|
エンティティ型の挿入、更新、および削除の 3 つの操作をすべてストアド プロシージャにマップしないと、マップされていない操作は実行時に失敗して UpdateException がスローされます。 |
前提条件
このチュートリアルを完了するには、CourseManager アプリケーションをビルドする必要があります。 詳細情報と手順については、「Entity Framework クイック スタート」を参照してください。 このアプリケーションをビルドした後に、2 つのエンティティ型をストアド プロシージャにマップして .edmx ファイルを変更します。
注 : |
---|
このドキュメントのチュートリアルのトピックの多くは CourseManager アプリケーションを開始点としているため、元の CourseManager コードを編集するのではなく、このチュートリアル用に CourseManager アプリケーションのコピーを使用することをお勧めします。 |
このチュートリアルでは、Visual Studio、.NET Framework、および Visual C# または Visual Basic のプログラミングの基本的なスキルがある読者を想定しています。
Person エンティティのストアド プロシージャへのマッピング
エンティティの挿入操作をストアド プロシージャにマップする際に、挿入される行の主キー値がサーバーで作成される場合は、その値をエンティティのキー プロパティにマップする必要があります。 この例では、新たに作成された主キーが InsertPerson ストアド プロシージャから結果セットの一部として返され、 エンティティ デザイナーの [<結果バインドの追加>] 機能を使用してエンティティ キー (PersonID) にマップされます。
注 : |
---|
整数値の出力パラメーターを返すストアド プロシージャに、挿入、更新、または削除の操作をマップする場合は、[処理行数パラメーター] チェックボックスが有効になります。パラメーターのこのチェックボックスをオンにすると、操作が呼び出されたときに戻り値がゼロの場合は、OptimisticConcurrencyException がスローされます。 |
Person エンティティをストアド プロシージャにマップするには
Visual Studio で CourseManager ソリューションを開きます。
ソリューション エクスプローラーで、School.edmx ファイルをダブルクリックします。
ADO.NET Entity Data Model デザイナー (エンティティ デザイナー) で School.edmx ファイルが開きます。
Person エンティティ型を右クリックし、[ストアド プロシージャ マッピング] をクリックします。
[マッピングの詳細] ウィンドウにストアド プロシージャのマッピングが表示されます。
[<挿入関数の選択>] をクリックします。
このフィールドは、概念モデルのエンティティ型にマップできる、ストレージ モデル内のストアド プロシージャが表示されるドロップダウン リストです。
ドロップダウン リストから [InsertPerson] を選択します。
ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。 矢印はマッピングの方向を表します (プロパティの値がストアド プロシージャのパラメーターに渡される)。
[<結果バインドの追加>] をクリックします。
フィールドが編集可能になります。
InsertPerson ストアド プロシージャにより返されたパラメーターの名前、「NewPersonID」を入力します。 Enter キーを押します。
既定では、NewPersonID はエンティティ キー PersonID にマップされます。 矢印はマッピングの方向を表します (結果列の値がプロパティに渡される)。
[<更新関数の選択>] をクリックし、表示される一覧で [UpdatePerson] をクリックします。
ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。
[<削除関数の選択>] をクリックし、表示される一覧で [DeletePerson] をクリックします。
ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。
以上で、Person エンティティ型の挿入、更新、および削除の操作がストアド プロシージャにマップされました。
OfficeAssignment エンティティのストアド プロシージャへのマッピング
この例では、OfficeAssignment エンティティ型をストアド プロシージャにマップします。 このマッピングでは、アプリケーションのコードで同時実行を簡単にチェックできるように、更新操作で [元の値を使用する] オプションを使用します。
OfficeAssignment エンティティをストアド プロシージャにマップするには
OfficeAssignment エンティティ型を右クリックし、[Stored Procedure Mapping] をクリックします。
[マッピングの詳細] ウィンドウにストアド プロシージャのマッピングが表示されます。
[<挿入関数の選択>] をクリックし、表示される一覧で [InsertOfficeAssignment] をクリックします。
ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。
[<結果バインドの追加>] をクリックします。
フィールドが編集可能になります。
「Timestamp」と入力します。
[Property/Value] 列で、Timestamp の横にある空のフィールドをクリックします。
フィールドが、InsertOfficeAssignment ストアド プロシージャから返される値をマップできるプロパティのドロップダウン リストになります。
ドロップダウン リストから [Timestamp] を選択します。
[<更新関数の選択>] をクリックし、表示される一覧で [UpdateOfficeAssignment] をクリックします。
ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。 [元の値を使用する] 列のマップされる各プロパティの横にチェック ボックスが表示されます。
[プロパティ] 列で、OrigTimestamp パラメーターに対応する空のフィールドをクリックし、表示される一覧で [Timestamp] をクリックします。
このマッピングは、パラメーター名がプロパティ名に正確に一致していないため、エンティティ デザイナーで既定のマッピングとして処理されません。
[元の値を使用する] 列で、Timestamp プロパティに対応するチェック ボックスをオンにします。
更新の際、データがデータベースに書き込まれるときに、データベースから読み取られた元の Timestamp プロパティの値が使用されます。 その値がデータベースの値と一致しない場合は、OptimisticConcurrencyException がスローされます。
[<結果バインドの追加>] をクリックします。
フィールドが編集可能になります。
"<結果バインドの追加>" を「Timestamp」に置き換えます。
[Property/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 に設定します。
viewInstructors Button コントロールをダブルクリックします。
CourseViewer フォームの分離コード ファイルが開きます。
viewInstructors_Click イベント ハンドラーに次のコードを追加します。
Dim instructorViewer As New InstructorViewer() instructorViewer.Visible = True
InstructorViewer instructorViewer = new InstructorViewer(); instructorViewer.Visible = true;
InstructorViewer フォームのデザイン ビューに戻ります。
viewOffices Button コントロールをダブルクリックします。
Form2 の分離コード ファイルが開きます。
viewOffices_Click イベント ハンドラーに次のコードを追加します。
Dim officeViewer As New OfficeViewer() officeViewer.Visible = True
OfficeViewer officeViewer = new OfficeViewer(); officeViewer.Visible = true;
ユーザー インターフェイスが完成しました。
インストラクターの情報の表示と更新
この手順では、InstructorViewer フォームにコードを追加して、インストラクターの情報を表示したり更新したりできるようにします。 具体的には、次の処理を行うコードを追加します。
Person 型 (インストラクター) に関する情報を返すクエリに DataGridView をバインドする。 オブジェクトをコントロールにバインドする方法の詳細については、「Binding Objects to Controls (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.People.Include("OfficeAssignment") _ .Where("it.HireDate is not null") _ .OrderBy("it.LastName") instructorGridView.DataSource = instructorQuery _ .Execute(MergeOption.OverwriteChanges) instructorGridView.Columns("EnrollmentDate").Visible = False instructorGridView.Columns("EnrollmentDate").Visible = False instructorGridView.Columns("OfficeAssignment").Visible = False instructorGridView.Columns("StudentGrades").Visible = False instructorGridView.Columns("Courses").Visible = False
// Initialize schoolContext. schoolContext = new SchoolEntities(); // Define the query to retrieve instructors. ObjectQuery<Person> instructorQuery = schoolContext.People .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; instructorGridView.Columns["OfficeAssignment"].Visible = false; instructorGridView.Columns["StudentGrades"].Visible = false; instructorGridView.Columns["Courses"].Visible = false;
InstructorViewer フォームのデザイン ビューに戻り、updateInstructor Button コントロールをダブルクリックします。
updateInstructor_Click イベント ハンドラーが分離コード ファイルに追加されます。
updateInstructor_Click イベント ハンドラーに、instructorGridView DataGridView コントロールでインストラクターの情報に加えられた変更を保存するコードを追加します。
' Save object changes to the database, display a ' message, and refresh the form. schoolContext.SaveChanges() MessageBox.Show("Change(s) saved to the database.") Me.Refresh()
// Save object changes to the database, display a // message, and refresh the form. schoolContext.SaveChanges(); MessageBox.Show("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.People.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.People .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"; }
このメソッドは、インストラクターの情報を返すクエリを実行し、結果を instructorList ComboBox コントロールにバインドします。
OfficeViewer_Load イベント ハンドラーに、オブジェクト コンテキストを初期化し、HireDate が null ではないすべての Person 型を返すクエリに ComboBox コントロールをバインドするメソッドを呼び出すコードを追加します。
schoolContext = New SchoolEntities() ExecuteInstructorQuery()
schoolContext = new SchoolEntities(); ExecuteInstructorQuery();
OfficeViewer フォームのデザイン ビューに戻り、instructorList ComboBox コントロールをダブルクリックします。
instructorList_SelectedIndexChanged イベント ハンドラーが分離コード ファイルに追加されます。
イベント ハンドラーに、選択されたインストラクターのオフィスの場所を ListBox コントロールに表示し、updateOffice Button コントロールを無効にするコードを追加します。 このコントロールは、選択されたオフィスの場所が変更されると有効になります。
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 フォームのデザイン ビューに戻り、updateOffice Button コントロールをダブルクリックします。
updateOffice_Click イベント ハンドラーが分離コード ファイルに追加されます。
officeLocation TextBox コントロールでオフィスの情報に加えられた変更を保存するコードを追加します。
Try 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 schoolContext.SaveChanges() MessageBox.Show("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 { 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); } schoolContext.SaveChanges(); MessageBox.Show("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 フォームのデザイン ビューに戻り、officeLocation TextBox コントロールをダブルクリックします。
officeLocation_TextChanged イベント ハンドラーが分離コード ファイルに追加されます。
選択されたオフィスの場所が変更された場合に updateOffice Button コントロールを有効にするコードを追加します。
' 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 イベント ハンドラーに次のコードを追加して、クライアントの変更が強制的にサーバーに適用されるか、instructorList ComboBox コントロールにバインドされているデータがデータベースから更新されるようにします。
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) schoolContext.SaveChanges() MessageBox.Show("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
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); schoolContext.SaveChanges(); MessageBox.Show("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.People.Include("OfficeAssignment") _
.Where("it.HireDate is not null") _
.OrderBy("it.LastName")
instructorGridView.DataSource = instructorQuery _
.Execute(MergeOption.OverwriteChanges)
instructorGridView.Columns("EnrollmentDate").Visible = False
instructorGridView.Columns("EnrollmentDate").Visible = False
instructorGridView.Columns("OfficeAssignment").Visible = False
instructorGridView.Columns("StudentGrades").Visible = False
instructorGridView.Columns("Courses").Visible = False
End Sub
Private Sub updateInstructor_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles updateInstructor.Click
' Save object changes to the database, display a
' message, and refresh the form.
schoolContext.SaveChanges()
MessageBox.Show("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.People
.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;
instructorGridView.Columns["OfficeAssignment"].Visible = false;
instructorGridView.Columns["StudentGrades"].Visible = false;
instructorGridView.Columns["Courses"].Visible = false;
}
private void updateInstructor_Click(object sender, EventArgs e)
{
// Save object changes to the database, display a
// message, and refresh the form.
schoolContext.SaveChanges();
MessageBox.Show("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 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
schoolContext.SaveChanges()
MessageBox.Show("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 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)
schoolContext.SaveChanges()
MessageBox.Show("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.People.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
{
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);
}
schoolContext.SaveChanges();
MessageBox.Show("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)
{
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);
schoolContext.SaveChanges();
MessageBox.Show("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.People
.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」を参照してください。