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

このトピックでは、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 エンティティをストアド プロシージャにマップするには

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

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

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

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

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

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

    フィールドが、EDM に含まれているストアド プロシージャのドロップダウン リストになります。

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

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

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

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

  7. "<結果バインドの追加>" を「NewPersonID」(InsertPerson ストアド プロシージャから返されるパラメータの名前) に置き換えます。Enter キーを押します。

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

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

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

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

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

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

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

一対一のアソシエーションの一方の End のエンティティ型がストアド プロシージャにマップされている場合、もう一方の End のエンティティもストアド プロシージャにマップする必要があります。この例では、OfficeAssignment エンティティ型に Person エンティティ型との一対一のアソシエーションがあるため、このエンティティ型もストアド プロシージャにマップします。このマッピングでは、アプリケーションのコードで同時実行を簡単にチェックできるように、更新操作で [元の値を使用する] オプションを使用します。

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

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

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

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

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

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

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

  4. "<結果バインドの追加>" の代わりに「Timestamp」と入力します。

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

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

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

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

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

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

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

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

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

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

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

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

  12. [Propery/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. viewInstructorsButton コントロールをダブルクリックします。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  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 イベント ハンドラに次のコードを追加して、クライアントの変更が強制的にサーバーに適用されるか、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();
    }
    
  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.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 ツールのタスク