当在数据集中作出更改之后,可以将更改传递给数据源。完成此操作时最常用的方法是调用数据适配器的 Update 方法。该方法依次通过数据表中的每个记录,确定需要什么类型的更新(更新、插入或删除),然后执行适当的命令(如果有)。

安全说明   当使用 CommandType 属性设置为 Text 的数据命令时,请对从客户端发送过来的信息进行仔细检查,然后再将它传递给数据库。恶意用户可能会试图发送(插入)修改过的或其他 SQL 语句,以获得未经授权的访问或破坏数据库。在将用户输入内容传输到数据库之前,应始终确认这些信息是有效的;如果可能的话,请始终使用参数化查询或存储过程,这是最佳措施。有关更多信息,请参见脚本利用

为了阐释如何进行更新,假设您的应用程序使用一个包含单个数据表的数据集。该应用程序从数据库中获取两行。在检索之后,内存中的数据表类似于下面这样:

(RowState)     CustomerID   Name             Status
(Unchanged)    c200         Robert Lyon      Good
(Unchanged)    c400         Nancy Buchanan    Pending

应用程序将 Nancy Buchanan 的状态更改为“Preferred”。作为这种更改的结果,该行 DataRow.RowState 属性的值从 Unchanged 更改为 Modified。第一行 RowState 属性的值保持为 Unchanged。数据表现在类似于下面这样:

(RowState)     CustomerID   Name             Status
(Unchanged)    c200         Robert Lyon      Good
(Modified)     c400         Nancy Buchanan    Preferred

应用程序现在调用 Update 方法,将数据集传送给数据库。该方法依次检查每一行。对于第一行,由于它从最初取自数据库后未经过更改,所以该方法不向数据库传送任何 SQL 语句。

但是对于第二行,Update 方法将自动调用适当的数据命令并将其传送给数据库。SQL 语句的具体语法取决于基础数据存储区所支持的 SQL 语言分支。但所传送的 SQL 语句具有下列值得注意的一般特征:

  • 所传送的 SQL 语句是一个 UPDATE 语句。因为 RowState 属性的值为 Modified,所以数据适配器知道使用 UPDATE 语句。

  • 所传送的 SQL 语句包含一个 WHERE 子句,它指示 UPDATE 语句的目标是 CustomerID = 'c400' 的行。由于 CustomerID 是目标表的主键,SELECT 语句的这一部分会将目标行与其他所有行区分开来。WHERE 子句的信息是从记录 (DataRowVersion.Original) 的初始版本导出的,以备在识别行所需的值已被更改的情况下使用。

  • 所传送的 SQL 语句包含 SET 子句,用以设置已修改列的新值。

    **注意   **如果数据适配器的 UpdateCommand 属性已设置为存储过程的名称,则适配器不会构造 SQL 语句,而是使用传入的适当参数调用存储过程。

传递参数

数据库中要更新的记录的值通常使用参数来进行传递。当数据适配器的 Update 方法执行 UPDATE 语句时,它需要填写参数值。它从 Parameters 集合中为适当的数据命令(在本例中是数据适配器中的 UpdateCommand 对象)获取这些值。

如果您已使用 Visual Studio 工具来生成数据适配器,则 UpdateCommand 对象将包含一个参数集合,这些参数对应于语句中的每个参数占位符。

每个参数的 SqlParameter.SourceColumn 属性指向数据表中的一列。例如,au_id 和 Original_au_id 参数的 SourceColumn 属性设置为包含作者 ID 的数据表中的任意一列。当适配器的 Update 方法运行时,它从所更新的记录中读取作者 ID 列,并将值填充到语句中。

在 UPDATE 语句中,您需要指定新值(将写入记录的值)和旧值(以便在数据库中查找要更新的记录)。因此,每个值都有两个参数:一个用于 SET 子句,另一个则用于 WHERE 子句。两个参数都从所更新的记录中读取数据,但根据参数的 SqlParameter.SourceVersion 属性,它们会获取该列值的不同版本。SET 子句的参数获取当前版本,而 WHERE 子句的参数获取初始版本。

**注意   **您还可以自己用代码在 Parameters 集合中设置值,这通常在数据适配器 RowChanging 事件的事件处理程序中进行。有关更多信息,请参见数据适配器命令中的参数

更新相关表

如果数据集包含多个表,则必须通过分别调用每个数据适配器的 Update 方法来逐个更新这些表。如果这些表具有父子关系,很可能需要以特定的顺序将更新发送到数据库。常见的情况是已经将父记录和相关子记录添加到数据集中,例如一个新的客户记录及一个或多个相关的订单记录。如果数据库本身强制关系完整性规则,那么当您在创建父记录之前将新的子记录发送到数据库时,数据库将引发错误。

相反,如果删除数据集中的相关记录,您通常需要以相反的顺序发送更新:先发送子表,然后发送父表。否则,数据库很可能引发错误,因为引用完整性规则将阻止您在相关子记录仍存在的情况下删除父记录。

为相关表发送更新的一般规则遵循以下顺序:

  1. 子表:删除记录。
  2. 父表:插入、更新和删除记录。
  3. 子表:插入和更新记录。

刷新数据集

在许多情况下,在更新数据源之后,将需要通过重新填充数据集来对其进行刷新。这样做有下列好处:

  • 数据集反映其他用户已对数据库作出的任何更改。
  • 它检索由数据库计算的列的值,例如标识列或具有默认值的列。
  • 它刷新数据集中记录上的时间戳,以备您需要将时间戳用于并发控制。

通过在调用适配器的 Update 方法之后调用其 Fill 方法,可以手动刷新数据集。

或者,您可以配置数据适配器,使其在执行更新后自动执行 SQL SELECT 语句或存储过程。这种情况下,数据适配器将为 UpdateCommandInsertCommand 对象创建两个 SQL 语句。第一个语句执行更新,第二个语句是一个用于刷新数据集的 SELECT 语句。

**注意   **为了让第二个 SELECT 语句执行,数据源必须像在 SQL Server 中一样支持批查询。

有关配置数据适配器的更多信息,请参见数据适配器配置向导

并发控制

由于数据集与数据源是分开的,您并不持有数据源中记录上的锁。因此,如果要更新数据库,并且如果维护并发控制对应用程序来说十分重要,则必须协调数据集中的记录和数据库中的记录。例如,您可能发现数据库中的记录在上一次填充数据集之后已经更改。在这种情况下,必须执行适合于应用程序的逻辑来指定如何处理数据库记录或数据集中已更改的记录。有关更多信息,请参见 ADO.NET 中的并发控制

请参见

Visual Studio .NET 中的数据集更新 | SqlParameter.SourceColumn 属性 | OleDbParameter.SourceColumn 属性 | SqlParameter.SourceVersion 属性 | OleDbParameter.SourceVersion 属性 | DataAdapter.Update 方法 | 数据适配器命令中的参数