處理 .NET Framework 資料庫應用程式中的並行例外狀況
注意
資料集和相關類別是 2000 年代初的舊版 .NET Framework 技術,可讓應用程式在應用程式與資料庫中斷連線時使用記憶體中的資料。 這些技術特別適用於可讓使用者修改資料並將變更保存回資料庫的應用程式。 雖然已證明資料集是非常成功的技術,但建議新的 .NET 應用程式使用 Entity Framework Core。 Entity Framework 提供更自然的方式,將表格式資料作為物件模型使用,而且具有更簡單的程式設計介面。
當兩位使用者同時嘗試變更資料庫中相同的資料時,就會引發並行例外狀況 (System.Data.DBConcurrencyException)。 在本逐步解說中,您會建立 Windows 應用程式,說明如何截取 DBConcurrencyException、找出造成錯誤的資料列,並了解如何處理它的策略。
本逐步解說會引導您完成下列流程:
建立一個新的 Windows Forms App (.NET Framework) 專案。
根據 Northwind 客戶資料表建立新的資料集。
建立具有 DataGridView 的表單以顯示資料。
使用來自 Northwind 資料庫中客戶資料表的資料來填滿資料集。
使用 [伺服器總管] 中的 [顯示資料表資料] 功能來存取客戶-資料表的資料並變更記錄。
將相同的記錄變更為不同的值、更新資料集,並嘗試將變更寫入資料庫,這會導致引發並行錯誤。
截取錯誤,然後顯示不同版本的記錄,讓使用者判斷是否繼續和更新資料庫,或取消更新。
必要條件
本逐步解說會使用 SQL Server Express LocalDB 和 Northwind 範例資料庫。
如果您沒有 SQL Server Express LocalDB,請從 SQL Server Express 下載頁面或透過 Visual Studio 安裝程式進行安裝。 在 Visual Studio 安裝程式中,您可以將 SQL Server Express LocalDB 安裝為資料儲存和處理工作負載的一部分,或安裝為個別元件。
請遵循下列步驟安裝 Northwind 範例資料庫:
在 Visual Studio 中,開啟 [SQL Server 物件總管] 視窗。 (SQL Server 物件總管會安裝為 Visual Studio 安裝程式中資料儲存和處理工作負載的一部分。)展開 [SQL Server] 節點。 以滑鼠右鍵按一下您的 LocalDB 執行個體,然後選取 [新增查詢]。
查詢編輯器視窗會隨即開啟。
將 Northwind Transact-SQL 指令碼複製到剪貼簿。 此 T-SQL 指令碼會從頭開始建立 Northwind 資料庫,並將資料填入其中。
將 T-SQL 指令碼貼入查詢編輯器中,然後選擇 [執行] 按鈕。
查詢很快就會完成執行,並建立 Northwind 資料庫。
建立新專案
從建立新的 Windows Forms 應用程式開始:
在 Visual Studio 的 [檔案] 功能表上,選取 [新]>[專案]。
在左側窗格中展開 Visual C# 或 Visual Basic,然後選取 [Windows 桌面]。
在中間窗格中,選取 [Windows Forms 應用程式] 專案類型。
將專案命名為 [InheritanceWalkthrough],然後選擇 [確定]。
ConcurrencyWalkthrough 專案會建立並新增至 方案總管,並在設計工具中開啟新的表單。
建立 Northwind 資料集
接下來,建立名為 NorthwindDataSet 的資料集:
在 [資料] 功能表上,請按一下 [新增新資料來源]。
[資料來源組態精靈] 隨即開啟。
在 [選擇資料來源類型] 畫面選取 [資料庫]。
從可用的連線清單中選取 Northwind 範例資料庫的連線。 如果連線清單中無法使用連線,請選取 [新連線]。
注意
如果您要連線到本機資料庫檔案,請在系統詢問您是否要將檔案新增至專案時,選取 [否]。
在 [將連接字串儲存至應用程式設定檔] 畫面上,選取 [下一步]。
展開 [資料表] 節點,然後選取 [客戶] 資料表。 資料集的預設名稱應該是 NorthwindDataSet。
選取 [完成] 以將事件新增至專案。
建立資料繫結 DataGridView 控制項
在本章節中,您會將 [客戶] 項目從 [資料來源] 視窗拖曳到您的 Windows Form,以建立 System.Windows.Forms.DataGridView。
若要開啟 [資料來源] 視窗,請按一下 [資料] 功能表上的 [顯示資料來源]。
在 [資料來源] 視窗中,展開 NorthwindDataSet 節點,並選取 [客戶] 資料表。
選取資料表節點上的向下鍵,然後在下拉式清單中選取 [DataGridView]。
將資料表拖曳到表單的空白區域。
名為 CustomersDataGridView 的 DataGridView 控制項,以及名為 CustomersBindingNavigator 的 BindingNavigator 會新增至繫結至 BindingSource 的表單。 接著,這會繫結至 NorthwindDataSet 中的客戶資料表。
測試表單
您現在可以測試表單,以確保表單到目前爲止都如預期般運作:
選取 [F5] 以執行應用程式。
表單上會出現 DataGridView 控制項,其中包含來自客戶資料表的資料。
在 [偵錯] 功能表上,選取 [停止偵錯]。
處理並行錯誤
處理錯誤的方式取決於控管應用程式的特定商務規則。 在本逐步解說中,我們會使用下列策略作為如何處理並行錯誤的範例。
應用程式向使用者呈現記錄的三個版本:
資料庫中目前的記錄
載入資料集的原始記錄
資料集中提議的變更
然後,使用者可以使用建議版本覆寫資料庫,或取消更新,並使用資料庫的新值重新整理資料集。
啟用並行錯誤的處理
建立自訂錯誤處理常式。
向使用者顯示選擇。
處理使用者的回應。
重新傳送更新,或重設資料集中的資料。
新增程式碼以處理並行例外狀況
當您嘗試執行更新並引發例外狀況時,通常想要使用引發的例外狀況所提供的資訊來執行某些動作。 在本章節中,您會新增嘗試更新資料庫的程式碼。 您也會處理可能引發的任何 DBConcurrencyException,以及任何其他例外狀況。
注意
逐步解說稍後會新增 CreateMessage
和 ProcessDialogResults
方法。
將下列程式碼新增至
Form1_Load
方法下方: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."); } }
取代
CustomersBindingNavigatorSaveItem_Click
方法以呼叫UpdateDatabase
方法,使其看起來如下所示:
向使用者顯示選擇
您剛才撰寫的程式碼會呼叫 CreateMessage
流程,向使用者顯示錯誤資訊。 在本逐步解說中,您會使用訊息方塊向使用者顯示不同版本的記錄。 這可讓使用者選擇要使用變更覆寫記錄,還是取消編輯。 一旦使用者選取訊息方塊中的選項 (按一下按鈕),回應就會傳遞至 ProcessDialogResult
方法。
將下列程式碼新增至 程式碼編輯器,以建立訊息。 在 UpdateDatabase
方法下方輸入下列程式碼:
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[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 void ProcessDialogResult(DialogResult response)
{
switch (response)
{
case DialogResult.Yes:
northwindDataSet.Merge(tempCustomersDataTable, true, MissingSchemaAction.Ignore);
UpdateDatabase();
break;
case DialogResult.No:
northwindDataSet.Merge(tempCustomersDataTable);
MessageBox.Show("Update cancelled");
break;
}
}
測試表單行為
您現在可以測試表單,以確保其如預期般運作。 若要模擬並行違規,請在填滿 NorthwindDataSet 之後變更資料庫中的資料。
選取 [F5] 以執行應用程式。
表單出現之後,請讓表單保持執行狀態,並切換至 Visual Studio IDE。
從 [檢視] 功能表中,選擇 [伺服器總管]。
在 [伺服器總管] 中,展開您應用程式所使用的連線,然後展開 [資料表] 節點。
以滑鼠右鍵按一下 [客戶] 資料表,然後選取 [顯示資料表資料]。
在第一筆記錄中 (ALFKI),將 ContactName 變更為 Maria Anders2。
注意
瀏覽至不同的資料列以提交變更。
切換至 ConcurrencyWalkthrough 的執行中表單。
在第一筆記錄中 (ALFKI),將 ContactName 變更為 Maria Anders1。
選取儲存按鈕。
引發並行錯誤,並出現訊息方塊。
選取 [否] 會取消更新,並使用目前在資料庫中的值來更新資料集。 選取 [是] 會將建議的值寫入資料庫。