共用方式為


逐步解說:處理並行存取例外狀況

更新:2007 年 11 月

當兩位使用者同時嘗試變更資料庫中的相同資料時,就會引發並行存取例外狀況 (DBConcurrencyException)。在這個逐步解說中,您將建立 Windows 應用程式,以說明如何攔截 DBConcurrencyException、找出造成錯誤的資料列,以及處理錯誤的策略。

這個逐步解說引導您執行下列程序:

  1. 建立新的 [Windows 應用程式] 專案。

  2. 根據 Northwind Customers 資料表,建立新資料集。

  3. 建立含有 DataGridView 的表單,以便顯示資料。

  4. 將 Northwind 資料庫中 Customers 資料表的資料填入資料集。

  5. 在填入資料集之後,使用 Visual Studio 中的 Visual Database Tools,直接存取 Customers 資料表,並變更資料錄。

  6. 然後在表單上將相同資料錄變更為不同的值、更新資料集,以及嘗試將變更寫入資料庫,這樣會引發並行存取錯誤。

  7. 攔截錯誤,然後顯示不同版本的資料錄,讓使用者決定是要繼續更新資料庫,還是取消更新。

必要條件

若要完成這個逐步解說,您必須要有:

注意事項:

根據目前使用的設定與版本,您所看到的對話方塊與功能表命令可能會與 [說明] 中所描述的不同。若要變更設定,請從 [工具] 功能表中選擇 [匯入和匯出設定]。如需詳細資訊,請參閱 Visual Studio 設定

建立新專案

建立新的 Windows 應用程式,開始這個逐步解說。

若要建立新的 Windows 應用程式專案

  1. 從 [檔案] 功能表中,建立新專案。

  2. 在 [專案類型] 窗格中,選取程式設計語言。

  3. 在 [範本] 窗格中,選取 [Windows 應用程式]。

  4. 將專案命名為 ConcurrencyWalkthrough,再按 [確定]。

    Visual Studio 隨即將專案加入至 [方案總管],並在設計工具中顯示新表單。

建立 Northwind 資料集

在本節中,您將建立名為 NorthwindDataSet 的資料集。

若要建立 NorthwindDataSet

  1. 選擇 [資料] 功能表上的 [加入新資料來源]。

    資料來源組態精靈隨即開啟。

  2. 請選取 [選擇資料來源類型] 頁面上的 [資料庫]。

  3. 從可用連接清單中,選取 Northwind 範例資料庫的連接;如果連接清單中沒有此連接,請按一下 [新增連接]。

    注意事項:

    如果您要連接到本機資料庫檔案,當詢問您是否要將檔案加入至專案時,請選取 [否]。

  4. 按一下 [將連接字串儲存到應用程式組態檔] 頁面上的 [下一步]。

  5. 展開 [資料表] 節點,並選取 Customers 資料表。資料集的預設名稱應該是 NorthwindDataSet。

  6. 按一下 [完成],將資料集加入至專案。

建立資料繫結 DataGridView 控制項

在本節中,您將從 [資料來源] 視窗將 [Customers] 項目拖曳至 Windows Form 上,以建立 DataGridView

若要建立繫結至 Customers 資料表的 DataGridView 控制項

  1. 從 [資料] 功能表中,選擇 [顯示資料來源] 以開啟 [資料來源] 視窗。

  2. 從 [資料來源] 視窗,展開 [NorthwindDataSet] 節點,並選取 [Customers] 資料表。

  3. 按一下資料表節點上的向下鍵,並從下拉式清單中選取 [DataGridView]。

  4. 將資料表拖曳至表單上的空白區域。

    名為 CustomersDataGridView 的 DataGridView 控制項以及名為 CustomersBindingNavigator 的 BindingNavigator 會加入到繫結至 BindingSource 的表單 (此來源接著會繫結至 NorthwindDataSet 中的 Customers 資料表)。

檢查點

您現在可以測試表單,確定到目前為止它的行為表現如預期般。

若要測試表單

  1. 請按 F5 以執行應用程式。

    此表單出現時,會包含 DataGridView 控制項,且控制項中填入了 Customers 資料表的資料。

  2. 從 [偵錯] 功能表中選擇 [停止偵錯]。

處理並行存取錯誤

如何處理錯誤,端視應用程式使用的特定商務規則 (Business Rule) 而定。在此逐步解說中,當引發並行違規之後,將會使用下列處理此並行存取錯誤的策略來當做說明:

應用程式會將資料錄的三個版本提供給使用者:

  • 資料庫中的目前資料錄。

  • 載入資料集中的原始資料錄。

  • 資料集中的建議變更。

然後,使用者將能夠以建議的版本覆寫資料庫,或是取消更新,並使用資料庫的新值來重新整理資料集。

若要啟用並行存取錯誤的處理

  1. 建立自訂錯誤處理常式。

  2. 將選擇顯示給使用者。

  3. 處理使用者的回應。

  4. 重新傳送更新,或重設資料集中的資料。

加入程式碼來處理並行存取例外狀況

當您嘗試執行更新,而引發例外狀況時,通常會想要對引發例外狀況所提供的資訊進行處理。

在本節中,您將會加入可嘗試更新資料庫的程式碼,以及處理任何可能會被引發的 DBConcurrencyException 及任何其他例外狀況。

注意事項:

此逐步解說的後面內容中,將會加入 CreateMessage 和 ProcessDialogResults 方法。

若要加入並行存取錯誤的錯誤處理

  1. 將以下程式碼加入至 Form1_Load 方法之下:

    Private Sub UpdateDatabase()
    
        Try
            Me.CustomersTableAdapter.Update(Me.NorthwindDataSet.Customers)
            MsgBox("Update successful")
    
        Catch dbcx As Data.DBConcurrencyException
            Dim response As Windows.Forms.DialogResult
    
            response = MessageBox.Show(CreateMessage(CType(dbcx.Row, NorthwindDataSet.CustomersRow)), _
                "Concurrency Exception", MessageBoxButtons.YesNo)
    
            ProcessDialogResult(response)
    
        Catch ex As Exception
            MsgBox("An error was thrown while attempting to update the database.")
        End Try
    End Sub
    
    private void UpdateDatabase()
    {
        try
        {
            this.customersTableAdapter.Update(this.northwindDataSet.Customers);
            MessageBox.Show("Update successful");
        }
        catch (DBConcurrencyException dbcx)
        {
            DialogResult response = MessageBox.Show(CreateMessage((NorthwindDataSet.CustomersRow)
                (dbcx.Row)), "Concurrency Exception", MessageBoxButtons.YesNo);
    
            ProcessDialogResult(response);
        }
        catch (Exception ex)
        {
            MessageBox.Show("An error was thrown while attempting to update the database.");
        }
    }
    
  2. 取代 CustomersBindingNavigatorSaveItem_Click 方法來呼叫 UpdateDatabase 方法,讓它看起來與下面類似:

    Private Sub CustomersBindingNavigatorSaveItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CustomersBindingNavigatorSaveItem.Click
        UpdateDatabase()
    End Sub
    
    private void customersBindingNavigatorSaveItem_Click(object sender, EventArgs e)
    {
        UpdateDatabase();
    }
    

將選擇顯示給使用者

您剛撰寫的程式碼會呼叫 CreateMessage 程序,向使用者顯示錯誤資訊。在這個逐步解說中,您將使用訊息方塊來將資料錄的不同版本顯示給使用者,並且允許使用者選擇是要利用變更覆寫資料錄或是取消編輯。一旦使用者選取訊息方塊上的選項 (按一下按鈕),回應便會傳送至 ProcessDialogResult 方法。

若要建立訊息以顯示給使用者

  • 將下列程式碼加入至 [程式碼編輯器],以建立訊息。在 UpdateDatabase 方法之下輸入此程式碼。

    Private Function CreateMessage(ByVal cr As NorthwindDataSet.CustomersRow) As String
        Return _
            "Database: " & GetRowData(GetCurrentRowInDB(cr), Data.DataRowVersion.Default) & vbCrLf & _
            "Original: " & GetRowData(cr, Data.DataRowVersion.Original) & vbCrLf & _
            "Proposed: " & GetRowData(cr, Data.DataRowVersion.Current) & vbCrLf & _
            "Do you still want to update the database with the proposed value?"
    End Function
    
    
    '--------------------------------------------------------------------------
    ' This method loads a temporary table with current records from the database
    ' and returns the current values from the row that caused the exception.
    '--------------------------------------------------------------------------
    Private TempCustomersDataTable As New NorthwindDataSet.CustomersDataTable
    
    Private Function GetCurrentRowInDB(ByVal RowWithError As NorthwindDataSet.CustomersRow) _
        As NorthwindDataSet.CustomersRow
    
        Me.CustomersTableAdapter.Fill(TempCustomersDataTable)
    
        Dim currentRowInDb As NorthwindDataSet.CustomersRow = _
            TempCustomersDataTable.FindByCustomerID(RowWithError.CustomerID)
    
        Return currentRowInDb
    End Function
    
    
    '--------------------------------------------------------------------------
    ' This method takes a CustomersRow and RowVersion 
    ' and returns a string of column values to display to the user.
    '--------------------------------------------------------------------------
    Private Function GetRowData(ByVal custRow As NorthwindDataSet.CustomersRow, _
        ByVal RowVersion As Data.DataRowVersion) As String
    
        Dim rowData As String = ""
    
        For i As Integer = 0 To custRow.ItemArray.Length - 1
            rowData += custRow.Item(i, RowVersion).ToString() & " "
        Next
    
        Return rowData
    End Function
    
    private string CreateMessage(NorthwindDataSet.CustomersRow cr)
    {
        return
            "Database: " + GetRowData(GetCurrentRowInDB(cr), DataRowVersion.Default) + "\n" +
            "Original: " + GetRowData(cr, DataRowVersion.Original) + "\n" +
            "Proposed: " + GetRowData(cr, DataRowVersion.Current) + "\n" +
            "Do you still want to update the database with the proposed value?";
    }
    
    
    //--------------------------------------------------------------------------
    // This method loads a temporary table with current records from the database
    // and returns the current values from the row that caused the exception.
    //--------------------------------------------------------------------------
    private NorthwindDataSet.CustomersDataTable tempCustomersDataTable = 
        new NorthwindDataSet.CustomersDataTable();
    
    private NorthwindDataSet.CustomersRow GetCurrentRowInDB(NorthwindDataSet.CustomersRow RowWithError)
    {
        this.customersTableAdapter.Fill(tempCustomersDataTable);
    
        NorthwindDataSet.CustomersRow currentRowInDb = 
            tempCustomersDataTable.FindByCustomerID(RowWithError.CustomerID);
    
        return currentRowInDb;
    }
    
    
    //--------------------------------------------------------------------------
    // This method takes a CustomersRow and RowVersion 
    // and returns a string of column values to display to the user.
    //--------------------------------------------------------------------------
    private string GetRowData(NorthwindDataSet.CustomersRow custRow, DataRowVersion RowVersion)
    {
        string rowData = "";
    
        for (int i = 0; i < custRow.ItemArray.Length ; i++ )
        {
            rowData = rowData + custRow.Item(i, RowVersion).ToString() + " ";
        }
        return rowData;
    }
    

處理使用者的回應

您也將需要程式碼來處理使用者對於訊息方塊的回應。此時有兩個選項:使用建議變更,覆寫資料庫中的目前資料錄,或是放棄本機變更,並以資料庫中目前資料錄重新整理資料表。如果使用者選擇 [是],就會在 preserveChanges 引數設為 true 的情況下呼叫 Merge 方法。這會讓更新得以成功,因為現在資料錄的原始版本與資料庫中的資料錄相符。

若要處理訊息方塊的使用者輸入

  • 在上節中所加入的程式碼下方,加入下列程式碼。

    ' This method takes the DialogResult selected by the user and updates the database 
    ' with the new values or cancels the update and resets the Customers table 
    ' (in the dataset) with the values currently in the database.
    
    Private Sub ProcessDialogResult(ByVal response As Windows.Forms.DialogResult)
    
        Select Case response
    
            Case Windows.Forms.DialogResult.Yes
                NorthwindDataSet.Customers.Merge(TempCustomersDataTable, True)
                UpdateDatabase()
    
            Case Windows.Forms.DialogResult.No
                NorthwindDataSet.Customers.Merge(TempCustomersDataTable)
                MsgBox("Update cancelled")
        End Select
    End Sub
    
    // This method takes the DialogResult selected by the user and updates the database 
    // with the new values or cancels the update and resets the Customers table 
    // (in the dataset) with the values currently in the database.
    
    private void ProcessDialogResult(DialogResult response)
    {
        switch (response)
        {
            case DialogResult.Yes:
                UpdateDatabase();
                break;
    
            case DialogResult.No:
                northwindDataSet.Merge(tempCustomersDataTable);
                MessageBox.Show("Update cancelled");
                break;
        }
    }
    

測試

您現在可以測試表單以確定它的行為表現如預期般。為了模擬並行違規,您必須在填入 NorthwindDataSet 後變更資料庫中的資料。

若要測試表單

  1. 按下 F5,執行應用程式。

  2. 表單出現之後,讓它繼續執行,並切換至 Visual Studio IDE。

  3. 從 [檢視] 功能表選擇 [伺服器總管]。

  4. 在 [伺服器總管] 中,展開應用程式使用的連接,然後展開 [資料表] 節點。

  5. 以滑鼠右鍵按一下 [Customers] 資料表,選取 [顯示資料表資料]。

  6. 在第一個資料錄 (ALFKI) 中,將 ContactName 變更為 Maria Anders2。

    注意事項:

    巡覽至不同的資料列,認可變更。

  7. 切換至 ConcurrencyWalkthrough 的執行中表單。

  8. 在表單上的第一筆資料錄 (ALFKI) 中,將 ContactName 變更為 Maria Anders1。

  9. 按一下 [儲存] 按鈕。

    將會引發並行存取錯誤,並出現訊息方塊。

  10. 按一下 [否] 會取消更新,並以資料庫中目前的值更新資料集,按一下 [是] 則會將建議值寫入資料庫。

請參閱

概念

資料的新功能

顯示資料概觀

其他資源

資料逐步解說

連接至 Visual Studio 中的資料

準備您的應用程式以接收資料

將資料擷取至您的應用程式中

顯示 Windows 應用程式之表單上的資料

在您的應用程式中編輯資料

驗證資料

儲存資料