チュートリアル: ストアド プロシージャへのエンティティのマッピング (Entity Data Model ツール)

このトピックでは、ADO.NET Entity Data Model デザイナー (エンティティ デザイナー) を使用してエンティティ型の挿入、更新、および削除の操作をストアド プロシージャにマップする方法について説明します。 エンティティ型の挿入、更新、および削除の操作では、システムによって自動的に生成される SQL ステートメントを使用することも (既定の方法)、特定のストアド プロシージャが使用されるように指定することもできます。 エンティティの作成、更新、および削除に使用されるアプリケーション コードは、データベースの更新にストアド プロシージャを使用するかどうかにかかわらず、同じです。

このチュートリアルでは、CourseManager アプリケーションで使用する .edmx ファイルを変更することによって、(概念モデルの) 2 つのエンティティ型を (ストレージ モデルの) ストアド プロシージャにマップします (詳細は、このトピックの「前提条件」を参照してください)。 エンティティ型の挿入、更新、および削除のためのコードも記述します。

Cc716679.note(ja-jp,VS.100).gif注 :
エンティティ型の挿入、更新、および削除の 3 つの操作をすべてストアド プロシージャにマップしないと、マップされていない操作は実行時に失敗して UpdateException がスローされます。

前提条件

このチュートリアルを完了するには、CourseManager アプリケーションをビルドする必要があります。 詳細情報と手順については、「Entity Framework クイック スタート」を参照してください。 このアプリケーションをビルドした後に、2 つのエンティティ型をストアド プロシージャにマップして .edmx ファイルを変更します。

Cc716679.note(ja-jp,VS.100).gif注 :
このドキュメントのチュートリアルのトピックの多くは CourseManager アプリケーションを開始点としているため、元の CourseManager コードを編集するのではなく、このチュートリアル用に CourseManager アプリケーションのコピーを使用することをお勧めします。

このチュートリアルでは、Visual Studio、.NET Framework、および Visual C# または Visual Basic のプログラミングの基本的なスキルがある読者を想定しています。

Person エンティティのストアド プロシージャへのマッピング

エンティティの挿入操作をストアド プロシージャにマップする際に、挿入される行の主キー値がサーバーで作成される場合は、その値をエンティティのキー プロパティにマップする必要があります。 この例では、新たに作成された主キーが InsertPerson ストアド プロシージャから結果セットの一部として返され、 エンティティ デザイナーの [<結果バインドの追加>] 機能を使用してエンティティ キー (PersonID) にマップされます。

Cc716679.note(ja-jp,VS.100).gif注 :
整数値の出力パラメーターを返すストアド プロシージャに、挿入、更新、または削除の操作をマップする場合は、[処理行数パラメーター] チェックボックスが有効になります。パラメーターのこのチェックボックスをオンにすると、操作が呼び出されたときに戻り値がゼロの場合は、OptimisticConcurrencyException がスローされます。

Person エンティティをストアド プロシージャにマップするには

  1. Visual Studio で CourseManager ソリューションを開きます。

  2. ソリューション エクスプローラーで、School.edmx ファイルをダブルクリックします。

    ADO.NET Entity Data Model デザイナー (エンティティ デザイナー) で School.edmx ファイルが開きます。

  3. Person エンティティ型を右クリックし、[ストアド プロシージャ マッピング] をクリックします。

    [マッピングの詳細] ウィンドウにストアド プロシージャのマッピングが表示されます。

  4. [<挿入関数の選択>] をクリックします。

    このフィールドは、概念モデルのエンティティ型にマップできる、ストレージ モデル内のストアド プロシージャが表示されるドロップダウン リストです。

  5. ドロップダウン リストから [InsertPerson] を選択します。

    ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。 矢印はマッピングの方向を表します (プロパティの値がストアド プロシージャのパラメーターに渡される)。

  6. [<結果バインドの追加>] をクリックします。

    フィールドが編集可能になります。

  7. InsertPerson ストアド プロシージャにより返されたパラメーターの名前、「NewPersonID」を入力します。 Enter キーを押します。

    既定では、NewPersonID はエンティティ キー PersonID にマップされます。 矢印はマッピングの方向を表します (結果列の値がプロパティに渡される)。

  8. [<更新関数の選択>] をクリックし、表示される一覧で [UpdatePerson] をクリックします。

    ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。

  9. [<削除関数の選択>] をクリックし、表示される一覧で [DeletePerson] をクリックします。

    ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。

以上で、Person エンティティ型の挿入、更新、および削除の操作がストアド プロシージャにマップされました。

OfficeAssignment エンティティのストアド プロシージャへのマッピング

この例では、OfficeAssignment エンティティ型をストアド プロシージャにマップします。 このマッピングでは、アプリケーションのコードで同時実行を簡単にチェックできるように、更新操作で [元の値を使用する] オプションを使用します。

OfficeAssignment エンティティをストアド プロシージャにマップするには

  1. OfficeAssignment エンティティ型を右クリックし、[Stored Procedure Mapping] をクリックします。

    [マッピングの詳細] ウィンドウにストアド プロシージャのマッピングが表示されます。

  2. [<挿入関数の選択>] をクリックし、表示される一覧で [InsertOfficeAssignment] をクリックします。

    ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。

  3. [<結果バインドの追加>] をクリックします。

    フィールドが編集可能になります。

  4. Timestamp」と入力します。

  5. [Property/Value] 列で、Timestamp の横にある空のフィールドをクリックします。

    フィールドが、InsertOfficeAssignment ストアド プロシージャから返される値をマップできるプロパティのドロップダウン リストになります。

  6. ドロップダウン リストから [Timestamp] を選択します。

  7. [<更新関数の選択>] をクリックし、表示される一覧で [UpdateOfficeAssignment] をクリックします。

    ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。 [元の値を使用する] 列のマップされる各プロパティの横にチェック ボックスが表示されます。

  8. [プロパティ] 列で、OrigTimestamp パラメーターに対応する空のフィールドをクリックし、表示される一覧で [Timestamp] をクリックします。

    このマッピングは、パラメーター名がプロパティ名に正確に一致していないため、エンティティ デザイナーで既定のマッピングとして処理されません。

  9. [元の値を使用する] 列で、Timestamp プロパティに対応するチェック ボックスをオンにします。

    更新の際、データがデータベースに書き込まれるときに、データベースから読み取られた元の Timestamp プロパティの値が使用されます。 その値がデータベースの値と一致しない場合は、OptimisticConcurrencyException がスローされます。

  10. [<結果バインドの追加>] をクリックします。

    フィールドが編集可能になります。

  11. "<結果バインドの追加>" を「Timestamp」に置き換えます。

  12. [Property/Value] 列で、Timestamp の横にある空のフィールドをクリックします。

    フィールドが、UpdateOfficeAssignment ストアド プロシージャから返される結果列をマップできるプロパティのドロップダウン リストになります。

  13. ドロップダウン リストから [Timestamp] を選択します。

  14. [<削除関数の選択>] をクリックし、表示される一覧で [DeleteOfficeAssignment] をクリックします。

    ストアド プロシージャのパラメーターとエンティティのプロパティとの既定のマッピングが表示されます。

以上で、OfficeAssignment エンティティ型の挿入、更新、および削除の操作がストアド プロシージャにマップされました。

ユーザー インターフェイスの構築

次に、CourseManager アプリケーションに 2 つのフォームを追加します。 インストラクターの情報を表示したり更新したりするためのインターフェイスを提供するフォームと、 オフィスの割り当てを表示したり更新したりするためのインターフェイスを提供するフォームです。

ユーザー インターフェイスを構築するには

  1. ソリューション エクスプローラーで [CourseManager] プロジェクトを右クリックし、[追加] をポイントして [新しい項目] をクリックします。

    [新しい項目の追加] ダイアログ ボックスが表示されます。

  2. [Windows フォーム] を選択し、フォーム名を InstructorViewer.vb または InstructorViewer.cs に設定して、[追加] をクリックします。

    新しいフォームがプロジェクトに追加され、フォーム デザイナーで開かれます。 フォーム名が InstructorViewer に、テキストが InstructorViewer に設定されます。

  3. DataGridView コントロールをツールボックスからフォームにドラッグし、[プロパティ] ウィンドウで [名前]instructorGridView に設定します。

  4. Button コントロールをツールボックスからフォームにドラッグします。 [名前]updateInstructor に、[テキスト]Update Instructor に設定します。

  5. Button コントロールをツールボックスからフォームにもう 1 つドラッグします。 [名前]viewOffices に、[テキスト]View Offices に設定します。

  6. ソリューション エクスプローラーで [CourseManager] プロジェクトを右クリックし、[追加] をポイントして [新しい項目] をクリックします。

    [新しい項目の追加] ダイアログ ボックスが表示されます。

  7. [Windows フォーム] を選択し、フォーム名を OfficeViewer.vb または OfficeViewer.cs に設定して、[追加] をクリックします。

    新しいフォームがプロジェクトに追加され、フォーム デザイナーで開かれます。 フォーム名が OfficeViewer に、テキストが OfficeViewer に設定されます。

  8. ComboBox コントロールをツールボックスからフォームにドラッグし、[名前]instructorList に設定します。

  9. TextBox コントロールをツールボックスからフォームにドラッグし、[名前]officeLocation に設定します。

  10. Button コントロールをツールボックスからフォームにドラッグします。 [名前]updateOffice に、[テキスト]Update Office に設定します。

  11. ソリューション エクスプローラーで、CourseViewer.vb または CourseViewer.cs をダブルクリックします。

    CourseViewer フォームのデザイン ビューが表示されます。

  12. Button コントロールをツールボックスからフォームにドラッグします。

  13. [プロパティ] ウィンドウで、Button[名前] プロパティを viewInstructors に、[テキスト] プロパティを View Instructors に設定します。

  14. viewInstructors Button コントロールをダブルクリックします。

    CourseViewer フォームの分離コード ファイルが開きます。

  15. viewInstructors_Click イベント ハンドラーに次のコードを追加します。

    Dim instructorViewer As New InstructorViewer()
    instructorViewer.Visible = True
    
    InstructorViewer instructorViewer = new InstructorViewer();
    instructorViewer.Visible = true;
    
  16. InstructorViewer フォームのデザイン ビューに戻ります。

  17. viewOffices Button コントロールをダブルクリックします。

    Form2 の分離コード ファイルが開きます。

  18. 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() が呼び出されたら、先ほどマップしたストアド プロシージャを使用してデータをデータベースに書き込む。

インストラクターの情報を表示および更新するには

  1. フォーム デザイナーで InstructorViewer フォームが開いた状態で、InstructorViewer フォームをダブルクリックします。

    InstructorViewer フォームの分離コード ファイルが開きます。

  2. 次の using (C#) ステートメントまたは Imports (Visual Basic) ステートメントを追加します。

    Imports System.Data.Objects
    Imports System.Data.Objects.DataClasses
    
    using System.Data.Objects;
    using System.Data.Objects.DataClasses;
    
  3. オブジェクト コンテキストを表すプロパティを InstructorViewer クラスに追加します。

    ' Create an ObjectContext instance based on SchoolEntity.
    Private schoolContext As SchoolEntities
    
    // Create an ObjectContext instance based on SchoolEntity.
    private SchoolEntities schoolContext;
    
  4. InstructorViewer_Load イベント ハンドラーに、オブジェクト コンテキストを初期化し、DataGridView コントロールのデータ ソースを、HireDatenull ではないすべての 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;
    
  5. InstructorViewer フォームのデザイン ビューに戻り、updateInstructor Button コントロールをダブルクリックします。

    updateInstructor_Click イベント ハンドラーが分離コード ファイルに追加されます。

  6. 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() が呼び出されたら、先ほどマップしたストアド プロシージャを使用してデータをデータベースに書き込む。

オフィスの情報を表示および更新するには

  1. フォーム デザイナーで OfficeViewer フォームが開いた状態で、OfficeViewer フォームをダブルクリックします。

    OfficeViewer フォームの分離コード ファイルが開きます。

  2. 次の using (C#) ステートメントまたは Imports (Visual Basic) ステートメントを追加します。

    Imports System.Data.Objects
    Imports System.Data.Objects.DataClasses
    
    using System.Data.Objects;
    using System.Data.Objects.DataClasses;
    
  3. オブジェクト コンテキストを表すプロパティを OfficeViewer クラスに追加します。

    ' Create an ObjectContext instance based on SchoolEntity.
    Private schoolContext As SchoolEntities
    
    // Create an ObjectContext instance based on SchoolEntity.
    private SchoolEntities schoolContext;
    
  4. フォームに次のメソッドを追加します。

    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 コントロールにバインドします。

  5. OfficeViewer_Load イベント ハンドラーに、オブジェクト コンテキストを初期化し、HireDatenull ではないすべての Person 型を返すクエリに ComboBox コントロールをバインドするメソッドを呼び出すコードを追加します。

    schoolContext = New SchoolEntities()
    ExecuteInstructorQuery()
    
    schoolContext = new SchoolEntities();
    ExecuteInstructorQuery();
    
  6. OfficeViewer フォームのデザイン ビューに戻り、instructorList ComboBox コントロールをダブルクリックします。

    instructorList_SelectedIndexChanged イベント ハンドラーが分離コード ファイルに追加されます。

  7. イベント ハンドラーに、選択されたインストラクターのオフィスの場所を 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;
    
  8. OfficeViewer フォームのデザイン ビューに戻り、updateOffice Button コントロールをダブルクリックします。

    updateOffice_Click イベント ハンドラーが分離コード ファイルに追加されます。

  9. 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;
    }
    
  10. OfficeViewer フォームのデザイン ビューに戻り、officeLocation TextBox コントロールをダブルクリックします。

    officeLocation_TextChanged イベント ハンドラーが分離コード ファイルに追加されます。

  11. 選択されたオフィスの場所が変更された場合に 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 フォームに追加します。

同時実行の競合を処理するには

  1. ソリューション エクスプローラーで、InstructorViewer.vb または InstructorViewer.cs をダブルクリックします。

    フォーム デザイナーでフォームが開きます。

  2. [View Offices] ボタンをダブルクリックします。

    InstructorViewer フォームの分離コード ファイルが開きます。

  3. 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;
    
  4. ソリューション エクスプローラーで、OfficeViewer.vb または OfficeViewer.cs をダブルクリックします。

    フォーム デザイナーでフォームが開きます。

  5. Button コントロールをツールボックスからフォームにドラッグします。 [名前]forceChanges に、[テキスト]Force Changes に設定します。

  6. [Force Changes] ボタンをダブルクリックします。

    Office Viewer フォームの分離コード ファイルが開きます。

  7. 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();
    }
    
  8. instructorList_SelectedIndexChanged イベント ハンドラーのコードの forceChanges = False (Visual Basic) または forceChanges = false; (C#) という行のコメントを解除して、新しいインストラクターが選択されると [Force Changes] ボタンが無効になるようにします。

  9. updateOffice_Click イベント ハンドラーのコードの forceChanges = True (Visual Basic) または forceChanges = true; (C#) という行のコメントを解除して、同時実行の競合が発生すると [Force Changes] ボタンが有効になるようにします。

  10. 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」を参照してください。

参照

その他のリソース

Entity Data Model ツールのシナリオ
Entity Data Model ツールのタスク