逐步解說:將實體對應至預存程序

本主題示範如何使用 ADO.NET 實體資料模型設計工具 (Entity Designer) 將實體類型的插入、更新和刪除作業對應至預存程序 (Stored Procedure)。實體類型的插入、更新和刪除作業可以使用系統 (預設值) 所自動產生的 SQL 陳述式 (Statement),或者可以使用開發人員所指定的預存程序。不論是否有使用預存程序來更新資料庫,建立、更新和刪除實體所用的應用程式程式碼都相同。

在本逐步解說中,您將修改 CourseManager 應用程式內使用的 Entity Data Model (EDM),藉以將兩個實體類型對應至預存程序 (如需詳細資訊,請參閱本主題稍後的<必要條件>章節)。您也將撰寫會插入、更新和刪除實體類型的程式碼。

必要條件

若要完成本逐步解說,必須建置 CourseManager 應用程式。如需詳細資訊和指示,請參閱 Entity Framework 快速入門。在建置此應用程式之後,您必須將兩個實體類型對應至預存程序,藉以修改其 EDM。

Cc716679.note(zh-tw,VS.100).gif注意:
因為本說明文件的許多逐步解說主題都從使用 CourseManager 應用程式開始,建議您為本逐步解說使用 CourseManager 應用程式的複本,而非編輯原始的 CourseManager 程式碼。

本逐步解說假設讀者具備以下基本能力:Visual Studio 和 .NET Framework 的使用能力,以及 Visual C# 或 Visual Basic 程式設計的能力。

將 Person 實體對應至預存程序

當您將實體的插入作業對應至預存程序時,如果伺服器為插入的資料列建立主索引鍵值,您就必須將這個值對應回實體的索引鍵屬性。在這個範例中,InsertPerson 預存程序會傳回新建立的主索引鍵做為預存程序之結果集 (Result Set) 的一部分。Entity Designer 的 [<加入結果繫結>] 功能是用來將主索引鍵對應至實體索引鍵 (PersonID)。

若要將 Person 實體對應至預存程序

  1. 在 Visual Studio 中開啟 CourseManager 方案。

  2. 按兩下 [方案總管] 中的 School.edmx 檔案。

    School.edmx 檔案隨即在 ADO.NET 實體資料模型設計工具 (Entity Designer) 中開啟。

  3. 以滑鼠右鍵按一下 [Person] 實體類型,然後選取 [預存程序對應]。

    預存程序對應隨即在 [對應詳細資料] 視窗中出現。

  4. 按一下 [<選取插入函式>]。

    欄位會變成 EDM 中所含之預存程序的下拉式清單。

  5. 從下拉式清單中選取 InsertPerson

    預存程序參數與實體屬性之間的預設對應隨即出現。請注意,箭頭表示對應方向:為預存程序參數提供屬性值。

  6. 按一下 [<加入結果繫結>]。

    欄位會變成可編輯的狀態。

  7. NewPersonID (由 InsertPerson 預存程序傳回的參數名稱) 來取代 [<加入結果繫結>],然後按 Enter

    根據預設,NewPersonID 會對應至 PersonID 實體索引鍵。請注意,箭頭表示對應方向:為屬性提供結果資料行的值。

  8. 按一下 [<選取更新函式>],然後從產生的下拉式清單中選取 UpdatePerson

    預存程序參數與實體屬性之間的預設對應隨即出現。

  9. 按一下 [<選取刪除函式>],然後從產生的下拉式清單中選取 DeletePerson

    預存程序參數與實體屬性之間的預設對應隨即出現。

Person 實體類型的插入、更新和刪除作業現在都已對應至預存程序了。

將 OfficeAssignment 實體對應至預存程序

如果位於一對一關聯一端的實體類型對應至預存程序,則位於關聯另一端的實體也必須對應至預存程序。在這個範例中,會將 OfficeAssignment 實體類型對應至預存程序,因為它與 Person 實體類型有一對一關聯。在這項對應中,則會在更新作業上使用 [使用原始值 ] 選項,以便於能夠對應用程式程式碼檢查並行存取。

若要將 OfficeAssignment 實體對應至預存程序

  1. 以滑鼠右鍵按一下 [OfficeAssignment] 實體類型,然後選取 [Stored Procedure Mapping]。

    預存程序對應隨即在 [對應詳細資料] 視窗中出現。

  2. 按一下 [<選取插入函式>],然後從產生的下拉式清單中選取 InsertOfficeAssignment

    預存程序參數與實體屬性之間的預設對應隨即出現。

  3. 按一下 [<加入結果繫結>]。

    欄位會變成可編輯的狀態。

  4. 輸入 Timestamp,以取代 [<加入結果繫結>]。

  5. Timestamp 旁邊的 [Propery/Value] 資料行中按一下空白欄位。

    欄位會變成我們可以對應由 InsertOfficeAssignment 預存程序傳回的值之屬性的下拉式清單。

  6. 從下拉式清單中選取 Timestamp

  7. 按一下 [<選取更新函式>],然後從產生的下拉式清單中選取 UpdateOfficeAssignment

    預存程序參數與實體屬性之間的預設對應隨即出現。核取方塊會在每個對應屬性旁邊的 [使用原始值] 資料行中顯示。

  8. 在對應至 OrigTimestamp 參數的 [屬性] 資料行中按一下空白欄位,然後從產生的下拉式清單中選取 Timestamp

    Entity Designer 沒有產生這項預設對應,因為參數名稱沒有完全符合屬性名稱。

  9. 在對應至 Timestamp 屬性的 [使用原始值] 資料行中選取方塊。

    當嘗試更新的時候,會在將資料寫回資料庫時使用原本從資料庫讀取的 Timestamp 屬性值。如果這個值與資料庫中的值不相符,則會擲回 OptimisticConcurrencyException

  10. 按一下 [<加入結果繫結>]。

    欄位會變成可編輯的狀態。

  11. Timestamp 來取代 [<加入結果繫結>]。

  12. Timestamp 旁邊的 [Propery/Value] 資料行中按一下空白欄位。

    欄位會變成我們可以對應由 UpdateOfficeAssignment 預存程序傳回的結果資料行之屬性的下拉式清單。

  13. 從下拉式清單中選取 Timestamp

  14. 按一下 [<選取刪除函式>],然後從產生的下拉式清單中選取 DeleteOfficeAssignment

    預存程序參數與實體屬性之間的預設對應隨即出現。

OfficeAssignment 實體類型的插入、更新和刪除作業現在都已對應至預存程序了。

建構使用者介面

接下來,您將加入兩個表單至 CourseManager 應用程式。一個表單提供檢視和更新講師資訊用的介面,另一個表單則提供檢視和更新辦公室分配用的介面。

若要建構使用者介面

  1. 以滑鼠右鍵按一下 [方案總管] 中的 CourseManager 專案,然後指向 [加入],再選取 [新增項目]。

    [加入新項目] 對話方塊隨即出現。

  2. 選取 [Windows Form],然後將表單的名稱設為 InstructorViewer.vb 或 InstructorViewer.cs,再按一下 [加入]。

    新的表單隨即加入至專案並在表單設計工具中開啟。這個表單的名稱已設為 InstructorViewer,而且文字已設為 InstructorViewer

  3. DataGridView 控制項從 [工具箱] 拖曳至表單,並在 [屬性] 視窗中將它的 [Name] 設為 instructorGridView

  4. Button 控制項從 [工具箱] 拖曳至表單。將它的 [Name] 設為 updateInstructor,以及它的 [Text] 設為 Update Instructor

  5. 將另一個 Button 控制項從 [工具箱] 拖曳至表單。將它的 [Name] 設為 viewOffices,以及它的 [Text] 設為 View Offices

  6. 以滑鼠右鍵按一下 [方案總管] 中的 CourseManager 專案,然後指向 [加入],再選取 [新增項目]。

    [加入新項目] 對話方塊隨即出現。

  7. 選取 [Windows Form],然後將表單的名稱設為 OfficeViewer.vb 或 OfficeViewer.cs,再按一下 [加入]。

    新的表單隨即加入至專案並在表單設計工具中開啟。這個表單的名稱已設為 OfficeViewer,而且文字已設為 OfficeViewer

  8. ComboBox 控制項從 [工具箱] 拖曳至表單,並將它的 [Name] 設為 instructorList

  9. TextBox 控制項從 [工具箱] 拖曳至表單,並將它的 [Name] 設為 officeLocation

  10. Button 控制項從 [工具箱] 拖曳至表單。將它的 [Name] 設為 updateOffice,以及它的 [Text] 設為 Update Office

  11. 按兩下 [方案總管] 中的 CourseViewer.vbCourseViewer.cs

    CourseViewer 表單的設計檢視隨即出現。

  12. Button 控制項從 [工具箱] 拖曳至表單。

  13. 在 [屬性] 視窗中將 Button 的 [Name] 屬性設為 viewInstructors,以及將 [Text] 屬性設為 View Instructors

  14. 按兩下 viewInstructorsButton 控制項。

    CourseViewer 表單之程式碼後置(Code-Behind) 的檔案隨即開啟。

  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 表單,讓您可以檢視和更新講師資訊。更具體地來說,程式碼會執行下列各項:

  • DataGridView 繫結至一個可傳回 Person 型別是講師之相關資訊的查詢。如需將物件繫結至控制項的詳細資訊,請參閱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 控制項的資料來源設為可傳回所有不具有 nullHireDatePerson 型別的查詢。

    ' 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 表單的設計檢視,然後按兩下 updateInstructorButton 控制項。

    updateInstructor_Click 事件處理常式會加入至程式碼後置的檔案。

  6. 將程式碼加入至 updateInstructor_Click 事件處理常式,它會儲存對 instructorGridViewDataGridView 控制項中的講師資訊所做的任何變更。

    ' 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";
    }
    

    這個方法會執行一個可傳回講師資訊並將結果繫結至 instructorListComboBox 控制項的查詢。

  5. OfficeViewer_Load 事件處理常式中加入程式碼,以便初始化物件內容,並呼叫一個將 ComboBox 控制項繫結至查詢的方法,而該查詢可傳回所有不具有 nullHireDatePerson 型別的查詢。

    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 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 表單的設計檢視,然後按兩下 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.vbInstructorViewer.cs

    該表單隨即在表單設計工具中開啟。

  2. 按兩下 [View Offices] 按鈕。

    InstructorViewer 表單之程式碼後置的檔案隨即開啟。

  3. 將下列程式碼加入至 viewOffices_Click 事件處理常式,以便在按下 [View Offices] 按鈕時會載入兩個 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.vbOfficeViewer.cs

    該表單隨即在表單設計工具中開啟。

  5. Button 控制項從 [工具箱] 拖曳至表單。將它的 [Name] 設為 forceChanges,以及它的 [Text] 設為 Force Changes

  6. 按兩下 [Force Changes] 按鈕。

    Office Viewer 表單之程式碼後置的檔案隨即開啟。

  7. 將下列程式碼加入至 forceChanges_Click 事件處理常式,以便對伺服器強制進行用戶端變更,或從資料庫重新整理繫結至 instructorListComboBox 控制項的資料。

    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 表單中更新辦公室位置,然後嘗試在其他 Demonstrate Conflict 表單中更新同一個辦公室位置。隨即會出現一個訊息方塊,告知您發生並行存取衝突。若要對資料庫強制進行 Demonstrate Conflict 表單的變更,請按一下 [Force Changes]。

列出程式碼

本章節包含 InstructorViewerOfficeViewer 表單之最終版本的程式碼後置檔案。

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 中預存程序之支援的詳細資訊,請參閱Stored Procedure Support (Entity Framework)。如需如何建置使用 Entity Framework 之應用程式的詳細資訊,請參閱Programming Guide (Entity Framework)

另請參閱

其他資源

ADO.NET 實體資料模型設計工具案例
Entity Data Model 工具工作