使用 DataAdapter 和 DataSet 更新資料庫
DataAdapter 的 Update 方法可呼叫來將 DataSet 中的更改解析回資料來源。與 Fill 方法類似,Update 方法將 DataSet 的例項和可選的 DataTable 物件或 DataTable 名稱用作引數。DataSet 例項是包含已作出的更改的 DataSet,而 DataTable 標識從其中檢索更改的表。
當呼叫 Update 方法時,DataAdapter 將分析已作出的更改並執行相應的命令(INSERT、UPDATE 或 DELETE)。當 DataAdapter 遇到對 DataRow 的更改時,它將使用 InsertCommand、UpdateCommand
Command 引數可用於為 DataSet 中每個已修改行的 SQL 語句或儲存過程指定輸入和輸出值。有關更多資訊,請參閱將引數用於 DataAdapter。
如果 DataTable 對映到單個數據庫表或從單個數據庫表生成,則可以利用 CommandBuilder
Update 方法會將更改解析回資料來源,但是自上次填充 DataSet 以來,其他客戶端可能已修改了資料來源中的資料。若要使用當前資料重新整理 DataSet,請再次使用 DataAdapter 填充 (Fill) DataSet。新行將新增到該表中,更新的資訊將併入現有行。
若要處理可能在 Update 操作過程中發生的異常,可以使用 RowUpdated 事件在這些異常發生時響應行更新錯誤(請參閱使用 DataAdapter 事件
注意 如果對 DataSet、DataTable 或 DataRow 呼叫 AcceptChanges,則將使某 DataRow 的所有 Original 值被該 DataRow 的 Current 值改寫。如果已修改將該行標識為唯一行的欄位值,那麼當呼叫 AcceptChanges 後,Original 值將不再匹配資料來源中的值。
以下示例演示如何通過顯式設定 DataAdapter 的 UpdateCommand 來執行對已修改行的更新。請注意,在 UPDATE 語句的 WHERE 子句中指定的引數設定為使用 SourceColumn 的 Original 值。這一點很重要,因為 Current 值可能已被修改,並且可能不匹配資料來源中的值。Original 值是曾用來從資料來源填充 DataTable 的值。
SqlClient
catDA.UpdateCommand = new SqlCommand("UPDATE Categories SET CategoryName = @CategoryName WHERE CategoryID = @CategoryID" , nwindConn);
catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");
SqlParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;
DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");
DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";
catDA.Update(catDS);
OleDb
catDA.UpdateCommand = new OleDbCommand("UPDATE Categories SET CategoryName = ? WHERE CategoryID = ?" , nwindConn);
catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName");
OleDbParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;
DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");
DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";
catDA.Update(catDS);
自動遞增列
如果來自資料來源的表包含自動遞增列,則可以使用由資料來源生成的值填充 DataSet 中的列,方法是通過以儲存過程輸出引數的形式返回自動遞增值並將其對映到表中的一列,或者使用 DataAdapter 的 RowUpdated 事件。有關示例,請參閱檢索“標識”或“自動編號”值。
但是,DataSet 中的值可能會與資料來源中的值不同步並導致意外的行為。例如,請考慮一個包含自動遞增主鍵列 CustomerID
的表。如果在該 DataSet 中新增兩個新客戶,它們將收到自動遞增的 CustomerId
值 1
和 2
。在向 DataAdapter 的 Update 方法傳遞第二個客戶行時,新新增的行會收到資料來源中的自動遞增 CustomerID
值 1
,該值與 DataSet 中的值 2
不匹配。當 DataAdapter 使用返回值填充 DataSet 中的行時,由於第一個客戶行的 CustomerID
已經是 1
,因此將發生約束衝突。
為了避免這種行為,建議在使用資料來源中的自動遞增列和 DataSet 中的自動遞增列時,在 DataSet 中建立 AutoIncrementStep 為 -1 且 AutoIncrementSeed 為 0 的列,並確保資料來源生成從 1 開始並以正步長值遞增的自動遞增標識值。這樣,DataSet 將為自動遞增值生成負數,這些負數不會與資料來源所生成的正自動遞增值發生衝突。另一種方法是使用 Guid 型別的列而不是自動遞增列。生成 Guid 值的演算法在 DataSet 中生成的 Guid 從不會與資料來源生成的 Guid 相同。有關定義 DataTable 中的列的更多資訊,請參閱定義資料表的架構。
插入、更新和刪除的排序
在許多情況下,以何種順序向資料來源傳送通過 DataSet 作出的更改是相當重要的。例如,如果已更新現有行的主鍵值並且添加了具有新主鍵值的新行,則務必要在處理插入之前處理更新。
可以使用 DataTable 的 Select 方法來返回僅引用具有特定 RowState 的 DataRow 陣列。然後可以將返回的 DataRow 陣列傳遞到 DataAdapter 的 Update 方法來處理已修改的行。通過指定要更新的行的子集,可以控制處理插入、更新和刪除的順序。
例如,以下程式碼確保首先處理表中已刪除的行,然後處理已更新的行,然後處理已插入的行。
// First process deletes.
custDA.Update(updTable.Select(null, null, DataViewRowState.Deleted));
// Next process updates.
custDA.Update(updTable.Select(null, null, DataViewRowState.ModifiedCurrent));
// Finally, process inserts.
custDA.Update(updTable.Select(null, null, DataViewRowState.Added));