ADONET 应用程序和基础数据源之间的交互基于一个具有双向信道的双体系结构您可以使用各个特定于提供程序的命令或批处理更新过程来访问数据源以读取和写入行在这两种情况下数据访问都会产生完全双向绑定并涉及各种不同的对象和方法您可以使用如 SqlCommand 和 OleDbCommand 等命令类来执行单个命令可使用数据适配器对象来下载断开连接的数据提交更新的行集虽然 数据集 是数据适配器用于返回和提交记录块的容器对象但各个命令通过数据读取器对象返回数据
更新是通过各个命令存储过程完成的通常托管提供程序理解的任何命令文本一般都被称为更新更新命令总是执行嵌入在语句正文中的新数据更新命令总是需要一个打开的连接可能还需要一个正在进行的事务处理或一个新的事务处理批处理更新则是一个略有不同的方法分支从最高的抽象级别来看您并不发出命令无论它可能有多么复杂取而代之的是您提交在客户端修改的当前行的快照并等待数据源批准批处理更新背后的关键概念是数据断开连接的概念您下载行表通常为数据集根据需要在客户端对它进行修改然后将这些行的新映像提交到数据库服务器您所作的是提交更改而不是执行一个对数据源创建更改的命令这就是更新(我在 July column 一文中讨论过这个问题)和批处理更新之间的本质区别
下图说明了 ADONET 的双更新体系结构
图 ADONET 应用程序和数据源之间的两个双向交互在进一步详细讨论 ADONET 批处理更新之前我需要阐明常常会导致某种误解的批处理更新模型的一个方面虽然更新和批处理更新在 ADONET 内的实际实现方面有着本质的区别但它们遵循的是同一个更新模型更新和批处理更新都是通过直接的并且特定于提供程序的语句来完成的当然由于批处理更新通常涉及到更多的行所以这些语句会被组合为一个批处理调用批处理更新会对目标数据集的行进行从头到尾的循环只要发现更新的行就会发出适当的更新命令(INSERTDELETE 或 UPDATE)对更新的行进行通信时将运行一个预定义的直接 SQL 命令从本质上来说这就是批处理更新
这个过程是理所当然的实际上如果批处理更新使用完全不同的更新模型就需要来自数据源的特殊支持(这正是向 SQL Server 提交 XML updategram 时发生的情况)批处理更新只是一个用来简化多个行更新提交的客户端提供的软件机制在任何情况下每个新行提交总是通过数据源直接命令的正常通道完成的
到目前为止本文只提及了 SQL 命令但这些提及的内容都明确表明了 ADO 批处理更新实现和 ADONET 批处理更新实现之间的一个重要区别在 ADO 中批处理更新只可能发生在基于 SQL 的数据源上而在 ADONET 中批处理更新则可能发生在任何种类的托管提供程序上其中包括那些不应该通过 SQL 查询语言公开其数据的托管提供程序现在我们可以开始讨论 ADONET 批处理更新编程的关键内容了
准备用于提交的数据集
ADONET 批处理更新通过数据适配器对象的 更新 方法进行数据只能以每个表为基础进行提交如果您调用 更新 时没有指定表名则使用 Table 这个默认的表名如果不存在具有该名称的表则会产生异常更新 首先检查每个表行的 RowState 属性然后为所指定表中的每个插入行更新行或删除行准备自定义的 INSERTUPDATE 或 DELETE 语句
更新 方法有几个超载它可以采用数据集和数据表提供的对某个数据表甚至是一个 DataRow 对象数组该方法会返回一个整数值即成功更新的行数
为了最大限度地减少网络通信通常会对正在操作的数据集的一个子集调用 更新毫无疑问这个子集只包含当时已修改的行您可以通过调用数据集的 GetChanges 方法来获得这样的子集
if (dsHasChanges())
{
DataSet dsChanges = dsGetChanges();
adapterUpdate(dsChanges MyTable);
}
另外您可以使用 HasChanges 方法检查数据集是否发生了更改HasChanges 返回一个布尔值
GetChanges 返回的数据集包含当时已插入删除或修改的行但这里所说的当时是什么时间呢?这正是 ADONET 批处理更新比较复杂的一个方面必须与表行的当前状态一起处理
行的状态
数据表 中的每一行都是通过 DataRow 对象呈现的DataRow 对象主要是作为父 数据表 对象的 Rows 集合的一个元素而存在的从概念上来看数据库行固有地链接到了某个给定表的结构就是由于这个原因ADONET 中的 DataRow 类不提供公用构造函数创建新 DataRow 对象的唯一方式是借助于对 数据表 对象的某个实时实例调用名为 NewRow 的方法刚刚创建好的行还不属于父表的 Rows 集合但该行与此集合的关系决定了该行的状态下表显示了 RowState 属性的一些可取值这些值组合在了 DataRowState 枚举中
Added
该行已添加到表中
Deleted
该行已标记为从父表删除
Detached
该行已创建但尚未添加到表中或者该行已从表行的集合中删除
Modified
该行中的某些列已更改
Added
该行已添加到表中
Unchanged
在创建后或上次调用 AcceptChanges 方法后未对该行进行任何更改
每一行的 RowState 属性都会影响 HasChanges 方法的返回值以及 GetChanges 返回的子数据集的内容
从这些可取值的范围可以看出RowState 的值主要取决于对行已经执行的操作ADONET 表基于两个方法 - AcceptChanges 和 RejectChanges - 来实现类似事务处理的提交模型从数据源下载表时或在内存中新建表时所有行都是没有更改的您输入的所有更改不会立即变为永久性更改随时都可以通过调用 RejectChanges 来回滚更改您可以在三个级别调用 RejectChanges 方法
; 在数据集级别上可拒绝所有更改(无论是什么更改)
; 在数据表级别上可取消某个表中的所有更改
; 在某个特定的行级别上可还原到该行以前的状态
方法 AcceptChanges 能够提交所有正在进行的更改它使得数据集会将当前值接受为新的原始值因此所有挂起的更改都被清除与 RejectChanges 一样也可以对整个数据集某个表或某个行调用 AcceptChanges
当您开始一个批处理更新操作时只会考虑提交那些标记为 AddedDeleted 和 Modified 的行如果您恰好在批处理更新之前调用了 AcceptChanges则对数据源不进行任何持久更改
另一方面一旦批处理更新操作成功完成您必须调用 AcceptChanges 来清除挂起的更改并将当前数据集值标记为原始值注意如果省略了最后对 AcceptChanges 的调用数据集中则会保留挂起的更改从而导致在下次进行批处理更新时重新发出这些更改
// Get changes in the DataSet
dsChanges = dsGetChanges();
// Performs the batch update for the given table
daUpdate(dsChanges strTable);
// Clears any pending change in memory
dsAcceptChanges();
上面的代码说明了 ADONET 批处理更新背后的三个主要步骤
如果从数据集表中删除行请注意您使用的方法是 删除 还是 移除删除 方法会通过将行标记为 删除执行逻辑删除而 移除 方法则从 Rows 集合中物理删除该行因此通过 移除 删除的行不会标记为删除因此在后面的批处理更新期间也不会被处理如果您的最终删除目标是从数据源删除行则应使用 删除
更新的深入内容
有三个操作可改变表的状态
; 插入一个新行
; 删除一个现有的行
; 更新一个现有的行
对于其中的每一个关键操作数据适配器都会定义一个作为属性公开的自定义的命令对象这样的属性包括 InsertCommandDeleteCommand 和 UpdateCommand程序员负责为这些属性分配有意义的命令对象例如SqlCommand 对象
仅提供的 InsertCommandDeleteCommand 和 UpdateCommand 属性就代表了从 ADO 到 ADONET 的巨大突破利用这种属性您可以对内存中的更新提交到数据库服务器的方式进行前所未有的控制如果您不满意 ADONET 生成的更新代码现在则可以修改这些更新代码而不会否定批处理更新的整体特性使用 ADO 的时候您对库静默生成的 SQL 命令毫无控制权而在 ADONET 中利用公开显示的命令对象您可以使用更符合用户期望的自定义存储过程或 SQL 语句来应用更新特别是您可以对交叉引用的表使用批处理更新系统甚至可以诸如 Active Directory; 或 Indexing Services 这样的非 SQL 数据提供程序为目标
更新命令应该针对表中每个更改的行运行并且必须非常通用以适应不同的值对于这种任务非常适合使用命令参数只要您可以将它们绑定到数据库列的值ADONET 参数对象公开两个用于这种绑定的属性例如 SourceColumn 和 SourceVersion尤其是 SourceColumn它表示一种指示参数值的间接方式您可以使用列名设置 SourceColumn 属性并且使批处理更新机制不时地提取有效值而不是使用 Value 属性