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 映射到单个数据库表或从单个数据库表生成,则可以利用 DbCommandBuilder 对象自动生成 DataAdapter 的 DeleteCommand、InsertCommand 和 UpdateCommand。有关更多信息,请参见自动生成命令。
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 以确保正确解析重复行。有关更多信息,请参见为表定义主键。 |
要处理在调用 Update 方法时可能发生的异常,可以使用 RowUpdated 事件响应更新行时发生的错误(请参见使用 DataAdapter 事件),也可以在调用 Update 之前将 DataAdapter.ContinueUpdateOnError 设置为 true,并在更新完成后响应特定行的 RowError 属性中存储的错误信息(请参见添加和读取行错误信息)。
注意 |
---|
如果对 DataSet、DataTable 或 DataRow 调用 AcceptChanges,则将使 DataRow 的所有 Original 值都将被重写为该 DataRow 的 Current 值。如果已修改将该行标识为唯一行的字段值,那么当调用 AcceptChanges 后,Original 值将不再匹配数据源中的值。 |
示例
以下示例演示如何通过显式设置 DataAdapter 的 UpdateCommand 来执行对已修改行的更新。请注意,在 UPDATE 语句的 WHERE 子句中指定的参数设置为使用 SourceColumn 的 Original 值。这一点很重要,因为 Current 值可能已被修改,并且可能不匹配数据源中的值。Original 值是曾用来从数据源填充 DataTable 的值。
' Assumes connection is a valid SqlConnection. Dim adapter As SqlDataAdapter = New SqlDataAdapter( _ "SELECT CategoryID, CategoryName FROM 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 dataSet As DataSet = New DataSet adapter.Fill(dataSet, "Categories") Dim row As DataRow = dataSet.Tables("Categories").Rows(0) row("CategoryName") = "New Category" adapter.Update(dataSet, "Categories")
// Assumes connection is a valid SqlConnection. 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; DataSet dataSet = new DataSet(); dataAdpater.Fill(dataSet, "Categories"); DataRow row = dataSet.Tables["Categories"].Rows[0]; row ["CategoryName"] = "New Category"; dataAdpater.Update(dataSet, "Categories");
自动递增列
如果来自数据源的表包含自动递增列,则可以通过以下方法填充 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 中的列的更多信息,请参见定义 DataTable 的架构。
插入、更新和删除的排序
在许多情况下,以何种顺序向数据源发送通过 DataSet 作出的更改是相当重要的。例如,如果已更新现有行的主键值并且添加了具有新主键值的新行,则务必要在处理插入之前处理更新。
可以使用 DataTable 的 Select 方法来返回仅引用具有特定 RowState 的 DataRow 数组。然后可以将返回的 DataRow 数组传递到 DataAdapter 的 Update 方法来处理已修改的行。通过指定要更新的行的子集,可以控制处理插入、更新和删除的顺序。
示例
例如,以下代码确保首先处理表中已删除的行,然后处理已更新的行,然后处理已插入的行。