DataAdapter 的 Update 方法可調用來將 DataSet 中的更改解析回數據源。與 Fill 方法類似,Update 方法將 DataSet 的實例和可選的 DataTable 對象或 DataTable 名稱用作參數。DataSet 實例是包含已作出的更改的 DataSet,而 DataTable 標識從其中檢索更改的表。
當調用 Update 方法時,DataAdapter 將分析已作出的更改并執行相應的命令(INSERT、UPDATE 或 DELETE)。當 DataAdapter 遇到對 DataRow 的更改時,它將使用 InsertCommand、UpdateCommand 或 DeleteCommand 來處理該更改。這樣,您就可以通過在設計時指定命令語法并在可能時通過使用存儲過程來盡量提高 ADO.NET 應用程序的性能。在調用 Update 之前,必須顯式設置這些命令。如果調用了 Update 但不存在用于特定更新的相應命令(例如,不存在用于已刪除行的 DeleteCommand),則將引發異常。
Command 參數可用于為 DataSet 中每個已修改行的 SQL 語句或存儲過程指定輸入和輸出值。有關更多信息,請參閱將參數用于 DataAdapter。
如果 DataTable 映射到單個數據庫表或從單個數據庫表生成,則可以利用 CommandBuilder 對象自動生成 DataAdapter 的 DeleteCommand、InsertCommand 和 UpdateCommand。有關更多信息,請參閱自動生成的命令。
Update 方法會將更改解析回數據源,但是自上次填充 DataSet 以來,其他客戶端可能已修改了數據源中的數據。若要使用當前數據刷新 DataSet,請再次使用 DataAdapter 填充 (Fill) DataSet。新行將添加到該表中,更新的信息將并入現有行。
若要處理可能在 Update 操作過程中發生的異常,可以使用 RowUpdated 事件在這些異常發生時響應行更新錯誤(請參閱使用 DataAdapter 事件),或者可以在調用 Update 之前將 DataAdapter.ContinueUpdateOnError 設置為 true,然后在 Update 完成時響應存儲在特定行的 RowError 屬性中的錯誤信息(請參閱添加和讀取行錯誤信息)。
注意 如果對 DataSet、DataTable 或 DataRow 調用 AcceptChanges,則將使某 DataRow 的所有 Original 值被該 DataRow 的 Current 值改寫。如果已修改將該行標識為唯一行的字段值,那么當調用 AcceptChanges 后,Original 值將不再匹配數據源中的值。 以下示例演示如何通過顯式設置 DataAdapter 的 UpdateCommand 來執行對已修改行的更新。請注意,在 UPDATE 語句的 WHERE 子句中指定的參數設置為使用 SourceColumn 的 Original 值。這一點很重要,因為 Current 值可能已被修改,并且可能不匹配數據源中的值。Original 值是曾用來從數據源填充 DataTable 的值。
SqlClient [Visual Basic] Dim catDA As SqlDataAdapter = New SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)
catDA.UpdateCommand = New SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " & _ "WHERE CategoryID = @CategoryID", nwindConn)
catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")
Dim workParm As SqlParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int) workParm.SourceColumn = "CategoryID" workParm.SourceVersion = DataRowVersion.Original
Dim catDS As DataSet = New DataSet catDA.Fill(catDS, "Categories")
Dim cRow As DataRow = catDS.Tables("Categories").Rows(0) cRow("CategoryName") = "New Category"
catDA.Update(catDS) [C#] SqlDataAdapter catDA = new SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);
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 [Visual Basic] Dim catDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)
catDA.UpdateCommand = New OleDbCommand("UPDATE Categories SET CategoryName = ? " & _ "WHERE CategoryID = ?" , nwindConn)
catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName")
Dim workParm As OleDbParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer) workParm.SourceColumn = "CategoryID" workParm.SourceVersion = DataRowVersion.Original
Dim catDS As DataSet = New DataSet catDA.Fill(catDS, "Categories")
Dim cRow As DataRow = catDS.Tables("Categories").Rows(0) cRow("CategoryName") = "New Category"
catDA.Update(catDS) [C#] OleDbDataAdapter catDA = new OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);
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 方法來處理已修改的行。通過指定要更新的行的子集,可以控制處理插入、更新和刪除的順序。
例如,以下代碼確保首先處理表中已刪除的行,然后處理已更新的行,然后處理已插入的行。
[Visual Basic] Dim updTable As DataTable = custDS.Tables("Customers")
' First process deletes. custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.Deleted))
' Next process updates. custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent))
' Finally, process inserts. custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.Added)) [C#] DataTable updTable = custDS.Tables["Customers"];
// 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));
|