以 DataAdapter 更新資料來源 (ADO.NET)
更新: November 2007
呼叫 DataAdapter 的 Update 方法,可將 DataSet 的變更解析回資料來源。Update 方法類似 Fill 方法,會將 DataSet 的執行個體以及選擇性 (Optional) DataTable 物件或 DataTable 名稱做為引數。DataSet 執行個體是包含已進行之變更的 DataSet,而 DataTable 則識別要從中擷取變更的資料表。如果沒有指定任何 DataTable,就會使用 DataSet 中的第一個 DataTable。
當您呼叫 Update 方法時,DataAdapter 會分析已進行的變更,並執行適當的命令 (INSERT、UPDATE 或 DELETE)。當 DataAdapter 發現 DataRow 的變更時,它會使用 InsertCommand、UpdateCommand 或 DeleteCommand 來處理變更。如此可讓您於設計階段指定命令語法並盡量利用預存程序 (Stored Procedure),發揮 ADO.NET 應用程式的最高效能。您必須在呼叫 Update 之前明確地設定命令。如果呼叫 Update 之後,特定更新沒有適當命令可用 (例如,刪除的資料列沒有 DeleteCommand),便會擲回例外狀況 (Exception)。
注意事項: |
---|
如果您要使用 SQL Server 預存程序搭配 DataAdapter 來編輯或刪除資料,請務必不要在預存程序定義中使用 SET NOCOUNT ON。因為這樣會讓傳回的受影響資料列計數成為零,而 DataAdapter 會將它解譯為並行衝突。在此事件中,系統會擲回 DBConcurrencyException。 |
命令參數可用來針對 DataSet 內每個經過修改的資料列指定 SQL 陳述式或預存程序的輸入和輸出值。如需詳細資訊,請參閱 DataAdapter 參數 (ADO.NET)。
注意事項: |
---|
請務必瞭解刪除 DataTable 中的資料列與移除資料列之間的差異。當您呼叫 Remove 或 RemoveAt 方法時,系統會立即移除資料列。如果您之後傳遞 DataTable 或 DataSet 給 DataAdapter 並呼叫 Update,則後端 (Back End) 資料來源中的任何對應資料列將不會受到影響。當您使用 Delete 方法時,資料列會保留在 DataTable 中並標示為待刪除。如果您之後傳遞 DataTable 或 DataSet 給 DataAdapter 並呼叫 Update,系統就會刪除後端資料來源中的對應資料列。 |
如果您的 DataTable 對應至或產生自單一資料庫資料表,則可以利用 DbCommandBuilder 物件來自動產生 DeleteCommand、InsertCommand 和 UpdateCommand 物件。如需詳細資訊,請參閱 使用 CommandBuilders 產生命令 (ADO.NET)。
使用 UpdatedRowSource 將值對應至 DataSet
在呼叫 DataAdapter 的 Update 方法之後,您可以使用 DbCommand 物件的 UpdatedRowSource 屬性來控制從資料來源傳回的值要如何對應回 DataTable。您可以透過將 UpdatedRowSource 屬性設為其中一個 UpdateRowSource 列舉值,控制要忽略 DataAdapter 命令所傳回的輸出參數,還是要將這些參數套用至 DataSet 中已變更的資料列。您還能夠指定是否要將第一個傳回的資料列 (如果存在) 套用至 DataTable 中已變更的資料列。
下列表格說明 UpdateRowSource 列舉型別的各種值,以及這些值如何影響與 DataAdapter 搭配使用之命令的行為。
UpdatedRowSource 列舉型別 |
描述 |
---|---|
輸出參數和傳回結果集的第一個資料列都會對應至 DataSet 中已變更的資料列。 |
|
只有傳回結果集之第一個資料列內的資料會對應至 DataSet 中已變更的資料列。 |
|
將忽略任何輸出參數或傳回結果集的資料列。 |
|
只有輸出參數會對應至 DataSet 中已變更的資料列。 |
Update 方法會將您的變更解析回資料來源,但是自從您上次填入 DataSet 之後,其他用戶端可能也已經修改資料來源的資料。若要以目前的資料重新整理 DataSet,請使用 DataAdapter 和 Fill 方法。如此新資料列會加入資料表,而更新資訊也會合併入現有資料列。Fill 方法會藉由檢查 DataSet 中資料列的主索引鍵值以及 SelectCommand 所傳回的資料列,決定要加入新資料列或更新現有資料列。如果 Fill 方法發現 DataSet 中之資料列的主索引鍵值符合 SelectCommand 所傳回結果中之資料列的主索引鍵值,它就會以 SelectCommand 所傳回之資料列中的資訊更新現有資料列,並將現有資料列的 RowState 設為 Unchanged。如果 SelectCommand 傳回之資料列的主索引鍵值與 DataSet 中資料列的任何主索引鍵值都不相符,Fill 方法就加入 RowState 設定為 Unchanged 的新資料列。
注意事項: |
---|
如果 SelectCommand 傳回 OUTER JOIN 的結果,DataAdapter 將不會為產生的 DataTable 設定 PrimaryKey 值。您必須自己定義 PrimaryKey,確保正確解析重複的資料列。如需詳細資訊,請參閱定義主索引鍵 (ADO.NET). |
若要處理呼叫 Update方法時可能會發生的例外狀況,您可以使用 RowUpdated 事件在發生例外狀況時回應資料列更新錯誤 (請參閱處理 DataAdapter 事件 (ADO.NET)),也可以在呼叫 Update 之前將 DataAdapter.ContinueUpdateOnError 設定為 true,然後在更新完成時回應儲存於特定資料列之 RowError 屬性中的錯誤資訊 (請參閱資料列錯誤資訊)。
請注意:在 DataSet、DataTable 或 DataRow 上呼叫 AcceptChanges 會導致 DataRow 的所有 Original 值都以 DataRow 的 Current 值覆寫。如果識別資料列為唯一的欄位值已經被修改,則在呼叫 AcceptChanges 之後,Original 值就不會再與資料來源內的值相符。此時,系統會在呼叫 DataAdapter 的 Update 方法期間,自動針對每個資料列呼叫 AcceptChanges。您可以先將 DataAdapter 的 AcceptChangesDuringUpdate 屬性設定為 false,或針對 RowUpdated 事件建立事件處理常式並將 Status 設定為 SkipCurrentRow,藉以在呼叫 Update 方法期間保留原始值。如需詳細資訊,請參閱合併 DataSet 內容 (ADO.NET)和處理 DataAdapter 事件 (ADO.NET)。
範例
下列範例示範如何明確設定 DataAdapter 的 UpdateCommand 並呼叫其 Update 方法,藉以更新已修改的資料列。請注意,在 UPDATE 陳述式之 WHERE 子句中指定的參數是設定為使用 SourceColumn 的 Original 值。這一點相當重要,因為 Current 值可能已經修改,而不符合資料來源中的值。Original 值是用來填入資料來源之 DataTable 的值。
Private Sub AdapterUpdate(ByVal connectionString As String)
Using connection As SqlConnection = New SqlConnection( _
connectionString)
Dim adapter As SqlDataAdapter = New SqlDataAdapter( _
"SELECT CategoryID, CategoryName FROM dbo.Categories", _
connection)
adapter.UpdateCommand = New SqlCommand( _
"UPDATE Categories SET CategoryName = @CategoryName " & _
"WHERE CategoryID = @CategoryID", connection)
adapter.UpdateCommand.Parameters.Add( _
"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")
Dim parameter As SqlParameter = _
adapter.UpdateCommand.Parameters.Add( _
"@CategoryID", SqlDbType.Int)
parameter.SourceColumn = "CategoryID"
parameter.SourceVersion = DataRowVersion.Original
Dim categoryTable As New DataTable
adapter.Fill(categoryTable)
Dim categoryRow As DataRow = categoryTable.Rows(0)
categoryRow("CategoryName") = "New Beverages"
adapter.Update(categoryTable)
Console.WriteLine("Rows after update.")
Dim row As DataRow
For Each row In categoryTable.Rows
Console.WriteLine("{0}: {1}", row(0), row(1))
Next
End Using
End Sub
private static void AdapterUpdate(string connectionString)
{
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlDataAdapter dataAdpater = new SqlDataAdapter(
"SELECT CategoryID, CategoryName FROM Categories",
connection);
dataAdpater.UpdateCommand = new SqlCommand(
"UPDATE Categories SET CategoryName = @CategoryName " +
"WHERE CategoryID = @CategoryID", connection);
dataAdpater.UpdateCommand.Parameters.Add(
"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");
SqlParameter parameter = dataAdpater.UpdateCommand.Parameters.Add(
"@CategoryID", SqlDbType.Int);
parameter.SourceColumn = "CategoryID";
parameter.SourceVersion = DataRowVersion.Original;
DataTable categoryTable = new DataTable();
dataAdpater.Fill(categoryTable);
DataRow categoryRow = categoryTable.Rows[0];
categoryRow["CategoryName"] = "New Beverages";
dataAdpater.Update(categoryTable);
Console.WriteLine("Rows after update.");
foreach (DataRow row in categoryTable.Rows)
{
{
Console.WriteLine("{0}: {1}", row[0], row[1]);
}
}
}
}
AutoIncrement 資料行
如果來自資料來源的資料表具有自動遞增的資料行,您就可以將這些資料行填入 DataSet 中,方法包括:將自動遞增值當成預存程序的輸出參數傳回並將它對應至資料表的資料行、傳回結果集 (由預存程序或 SQL 陳述式所傳回) 之第一個資料列中的自動遞增值,或使用 DataAdapter 的 RowUpdated 事件來執行其他 SELECT 陳述式。如需詳細資訊和範例,請參閱擷取識別或 Autonumber 值 (ADO.NET)。
插入、更新和刪除的順序
在許多情況下,以何種順序將透過 DataSet 所進行的變更傳送給資料來源是很重要的。例如,如果更新了現有資料列的主索引鍵值,也加入了以新主索引鍵值當做外部索引鍵的資料列,則先進行更新再執行插入是很重要的。
您可以使用 DataTable 的 Select 方法來傳回僅參考具有特定 RowState 之資料列的 DataRow 陣列。接著,您可以將傳回的 DataRow 陣列傳遞給 DataAdapter 的 Update 方法來處理已修改的資料列。您可指定要更新的資料列子集,藉以控制插入、更新和刪除的處理順序。
範例
例如,下列程式碼確保先處理資料表的已刪除資料列,接著處理已更新資料列,然後再處理已插入資料列。
Dim table As DataTable = dataSet.Tables("Customers")
' First process deletes.
dataSet.Update(table.Select(Nothing, Nothing, _
DataViewRowState.Deleted))
' Next process updates.
adapter.Update(table.Select(Nothing, Nothing, _
DataViewRowState.ModifiedCurrent))
' Finally, process inserts.
dataAdpater.Update(table.Select(Nothing, Nothing, _
DataViewRowState.Added))
DataTable table = dataSet.Tables["Customers"];
// First process deletes.
adapter.Update(table.Select(null, null, DataViewRowState.Deleted));
// Next process updates.
adapter.Update(table.Select(null, null,
DataViewRowState.ModifiedCurrent));
// Finally, process inserts.
adapter.Update(table.Select(null, null, DataViewRowState.Added));