剖析 ADO.NET 批处理更新(深入研究数据访问)
ADO.NET 应用程序和基础数据源之间的交互基于一个具有双向信道的双体系结构。您可以使用各个特定于提供程序的命令或批处理更新过程来访问数据源,以读取和写入行。在这两种情况下,数据访问都会产生完全双向绑定,并涉及各种不同的对象和方法。您可以使用如 SqlCommand 和 OleDbCommand 等命令类来执行单个命令。可使用数据适配器对象来下载断开连接的数据,提交更新的行集。虽然 “数据集” 是数据适配器用于返回和提交记录块的容器对象,但各个命令通过数据读取器对象返回数据。
更新是通过各个命令、存储过程完成的,通常,托管提供程序理解的任何命令文本一般都被称为更新。更新命令总是执行嵌入在语句正文中的新数据。更新命令总是需要一个打开的连接,可能还需要一个正在进行的事务处理或一个新的事务处理。批处理更新则是一个略有不同的方法分支。从最高的抽象级别来看,您并不发出命令,无论它可能有多么复杂。取而代之的是,您提交在客户端修改的当前行的快照,并等待数据源批准。批处理更新背后的关键概念是数据断开连接的概念。您下载行表,通常为数据集,根据需要在客户端对它进行修改,然后将这些行的新映像提交到数据库服务器。您所作的是提交更改,而不是执行一个对数据源创建更改的命令。这就是更新(我在 July column 一文中讨论过这个问题)和批处理更新之间的本质区别。
下图说明了 ADO.NET 的双更新体系结构。
图 1. ADO.NET 应用程序和数据源之间的两个双向交互
在进一步详细讨论 ADO.NET 批处理更新之前,我需要阐明常常会导致某种误解的批处理更新模型的一个方面。虽然更新和批处理更新在 ADO.NET 内的实际实现方面有着本质的区别,但它们遵循的是同一个更新模型。更新和批处理更新都是通过直接的并且特定于提供程序的语句来完成的。当然,由于批处理更新通常涉及到更多的行,所以这些语句会被组合为一个批处理调用。批处理更新会对目标数据集的行进行从头到尾的循环,只要发现更新的行,就会发出适当的更新命令(INSERT、DELETE 或 UPDATE)。对更新的行进行通信时,将运行一个预定义的直接 SQL 命令。从本质上来说,这就是批处理更新。
这个过程是理所当然的。实际上,如果批处理更新使用完全不同的更新模型,就需要来自数据源的特殊支持。(这正是向 SQL Server 2000 提交 XML updategram 时发生的情况。)批处理更新只是一个用来简化多个行更新提交的客户端提供的软件机制。在任何情况下,每个新行提交总是通过数据源直接命令的正常通道完成的。
到目前为止,本文只提及了 SQL 命令,但这些提及的内容都明确表明了 ADO 批处理更新实现和 ADO.NET 批处理更新实现之间的一个重要区别。在 ADO 中,批处理更新只可能发生在基于 SQL 的数据源上。而在 ADO.NET 中,批处理更新则可能发生在任何种类的托管提供程序上,其中包括那些不应该通过 SQL 查询语言公开其数据的托管提供程序。现在,我们可以开始讨论 ADO.NET 批处理更新编程的关键内容了。
准备用于提交的数据集
ADO.NET 批处理更新通过数据适配器对象的 “更新” 方法进行。数据只能以每个表为基础进行提交。如果您调用 “更新” 时没有指定表名,则使用 Table 这个默认的表名。如果不存在具有该名称的表,则会产生异常。“更新” 首先检查每个表行的 RowState 属性,然后为所指定表中的每个插入行、更新行或删除行准备自定义的 INSERT、UPDATE 或 DELETE 语句。
“更新” 方法有几个超载。它可以采用数据集和数据表提供的对、某个数据表、甚至是一个 DataRow 对象数组。该方法会返回一个整数值,即成功更新的行数。
为了最大限度地减少网络通信,通常会对正在操作的数据集的一个子集调用 “更新”。毫无疑问,这个子集只包含当时已修改的行。您可以通过调用数据集的 GetChanges 方法来获得这样的子集。
if (ds.HasChanges())
{
DataSet dsChanges = ds.GetChanges();
adapter.Update(dsChanges, "MyTable");
}
另外,您可以使用 HasChanges 方法检查数据集是否发生了更改。HasChanges 返回一个布尔值。
GetChanges 返回的数据集包含当时已插入、删除或修改的行。但这里所说的当时是什么时间呢?这正是 ADO.NET 批处理更新比较复杂的一个方面,必须与表行的当前状态一起处理。
返回页首
行的状态
“数据表” 中的每一行都是通过 DataRow 对象呈现的。DataRow 对象主要是作为父 “数据表” 对象的 Rows 集合的一个元素而存在的。从概念上来看,数据库行固有地链接到了某个给定表的结构。就是由于这个原因,ADO.NET 中的 DataRow 类不提供公用构造函数。创建新 DataRow 对象的唯一方式是借助于对 “数据表” 对象的某个实时实例调用名为 NewRow 的方法。刚刚创建好的行还不属于父表的 Rows 集合,但该行与此集合的关系决定了该行的状态。下表显示了 RowState 属性的一些可取值。这些值组合在了 DataRowState 枚举中。