为ADO程序设计的ADO.NET(一)
摘要:本文讨论如何以 ADO.NET 方式实现基本数据库操作,以及何时使用 ADO.NET 代替 ADO。 目录
.NET 中的数据访问
读取数据
DataSet、DataTable 和 Recordset
转换现有代码
更新数据
XML 扩展支持
总结
自若干年前推出开放式数据库连接 (ODBC) 应用程序编程接口 (API) 以来,出现了各种各样的数据库访问技术,而 ADO.NET 是其中最新的一种。在这过程中,发生了许多有趣的事。例如,COM 闯入数据库领域,开始培植 OLE DB 的殖民进程。然后,大致相当于 OLE DB 自动化版本的 ActiveX® Data Objects (ADO) 被选来统治 Windows® 数据库开发者的 Visual Basic® 和 ASP 社区。
通过 .NET,Microsoft 正在提供通用框架(即 Framework Class Library),其中将包括所有现有的 Windows API 甚至更多的内容。特别值得一提的是,它包括大量常用的库,而这些库现在需要通过各个 COM 对象分别获得。在这些库中,您会发现 XML 和 ADO 对象模型,它们被集成到了叫做 ADO.NET 的类子树中。
ADO.NET 事实上成为构建数据感知 .NET 应用程序的基础。和 ADO 不同的是,ADO.NET 遵循更通用的原则,不那么专门面向数据库。ADO.NET 集合了所有允许数据处理的类。这些类表示具有典型数据库功能(如索引、排序和视图)的数据容器对象。尽管 ADO.NET 是 .NET 数据库应用程序的权威解决方案,但从总体设计上来看,它不象 ADO 模型那样以数据库为中心,这是 ADO.NET 的一大特点。
ADO.NET 与 ADO 有很大差异。ADO.NET 是新的数据访问编程模型,需要开发人员的全面理解、投入和新思维。然而,一旦开始掌握 ADO.NET,您将意识到:原有的 ADO 技巧非常有助于您以不同、却更巧妙和可靠的方式来创建有效的应用程序和解决各种老问题。
在这篇文章的其余部分,我将集中介绍如何以 ADO.NET 方式实现基本的数据库操作。我想说明,在什么时候 ADO.NET 是比 ADO 更好的选择,而您最好在什么时候应放弃 ADO。ADO.NET 并不是将 ADO 改良以符合 .NET 基础结构而形成的。只要您看一下 ADO.NET 的语法、代码设计和移植,就会明白这一点。.NET 中的数据访问
在 ADO.NET 中访问数据源的方式由托管提供程序确定。从功能上讲,托管提供程序与 OLE DB 的提供程序非常相似,但有两个重要的不同之处。首先,管理提供程序在 .NET 环境中工作,通过 DataReader 和 DataTable 等 .NET 类检索和公开数据。其次,因为它们的体系结构针对 .NET 进行了优化,所以比较简单。
目前 ADO.NET 提供了两种托管提供程序:一种用于 SQL Server™ 7.0 或更高版本,另一种用于其他所有您可能已经安装的 OLE DB 提供程序。在这两种情况下您分别使用不同的类,但遵循相似的命名规则。除前缀外,名称都是相同的。前一种情况前缀为 SQL,后一种情况则是 ADO。
您应该使用 SQL 类访问 SQL Server 表,因为它们直接进入数据库服务器的内部 API,跳过了由 OLE DB 提供程序表示的中间层。ADO 类是 OLE DB 提供程序上的 .NET 接口,它们使用 COM Interop 桥进行工作。
ADO.NET 对象的初学者可参阅 Omri Gazitt 的文章介绍 ADO+:用于 Microsoft .NET 框架的数据访问服务(英文)和我的 ADO+ 推动数据种类的演变(英文)一文。前者技术性较强,针对 ADO.NET 程序模型提供了高水平的评注性概述。后者主要介绍 ADO.NET 的目标和它与 XML、脚本以及其他技术之间的联系。读取数据
需要从数据源中读取数据的 ADO.NET 应用程序首先要创建连接对象。根据目标提供程序的不同,该连接对象可以是 SQLConnection 或 ADOConnection。请记住,您可以使用 ADO.NET 类来连接到 SQL Server 数据库,但我们不建议这样做。其唯一的缺点是,您的代码要通过不必要的额外代码层。它先将 ADO 的托管提供程序调入,然后托管提供程序再调用 SQL Server OLE DB 提供程序。而 SQL Server 托管提供程序和 OLE DB 提供程序一样直接操作数据。
ADO 和 ADO.NET 连接对象之间的显著差异是:ADO.NET 连接不支持 CursorLocation 属性。请注意,这并不是一个文档错误,而是一个有争议的设计问题。为了突出以数据为中心的原则,ADO.NET 没有游标的显式实现。
在 ADO 中,您习惯了用游标从数据库或其他任何 OLE DB 兼容的数据源中抽取记录。您可以选择客户端或服务器游标,每种游标都有几个预先设定的游标类型。ADO.NET 则设计为从数据源中抽取数据,并提供新的编程接口来读取和分析数据。
在 ADO 中,您通过指定连接和命令文本来创建 Recordset 对象。对于游标的位置和类型,Recordset 有一定策略。您可以按下列方式之一读取数据: 在内存中创建选定记录的静态副本,然后在从数据源断开连接时根据需要处理这些记录。ADO 称之为静态游标。
通过快速、仅向前的只读游标来滚动数据,这种游标工作在记录的静态快照中。ADO 称之为只读游标。
通过服务器端的两种游标来访问数据,这些游标需要保持良好的连接,但您可以在各个不同层次上随时检测其他已连接的用户的更改。ADO 称它们为键集和动态游标。
前两种方式都在断开连接的记录集内工作,并从客户端缓存读取信息,这是它们的相似之处。另外,在面向 Web 的环境中和对于新的 n 层系统,这两种方式被证明是使用频率最高的。
在 ADO 中,以上所有这些方式与不同类型的游标相对应。您将在本文后面发现,虽然 ADO.NET 有很大不同,但它能实现您用 ADO 可实现的任何功能。只不过您的代码将从实际数据源及其物理存储媒介和格式中抽取数据。
ADO.NET 提供两个对象来处理从数据源中抽取的数据。它们是 DataSet 和 DataReader 对象。前者是记录在内存中的缓存,您可以从任何方向随意访问和修改。后者是高度优化的对象,专为以仅向前方式滚动只读记录而设计。请注意 DataSet 看起来象静态游标,但实际上,在 .NET 中与 ADO 只读游标相对应的是 DataReader 对象。
在 ADO.NET 中,不支持服务器端游标。然而,这不意味着您不能使用游标。您需要做的是在 .NET 中导入 ADO 类型库。在项目窗口的 References 节点上单击右键就行了。导入之后,您便可以开始在应用程序中使用本地 ADO 对象了。
尽管我承认下决心转向 .NET 是一件很难的事情,但我个人还是建议您考虑用 .NET 重写现有应用程序。可以把完全导入 ADO 作为迈向 .NET 的第一步,这无须投入太多的时间和资源。然而,请记住这只是漫漫长路上的第一步。这绝不是您迈向 .NET 的唯一一步。.NET 具有超值价值的的真正原因在于统一和一致的编程接口以及对本地类的广泛使用。您可以导入 COM 类型库,但导入 COM 类型库只能作为临时解决方案或者中间步骤,我们并不鼓励这样做。
使用 ADO.NET 时,应当充分考虑到它统一了数据容器类编程接口这一事实。无论您打算编写何种应用程序,Windows 窗体、Web 窗体还是 Web 服务,都可以通过同一组类来处理数据。不管在后端的数据源是 SQL Server 数据库、OLE DB、XML 文件还是一个数组,您都可以通过相同的方法和属性来滚动和处理它们的内容。
图 1:Solution Explorer 菜单
如果您坚持在 .NET 中使用 ADO,请准备面对一些副作用。例如,您需要额外的代码才能够从数据绑定控件中使用记录集。DataSet、DataTable 和 Recordset
在 ADO.NET 中,没有与 Recordset 对象直接对应的对象。最接近的是 DataTable 对象。尽管这两个对象的功能几乎一样,但它们在各自的框架中起不同的作用。
Recordset 是一个大型对象,具有许多 ADO 功能,但还是有所欠缺。Recordset 在很多方面性能优良,例如可创建性、断开连接时仍能工作、功能丰富等等。但是,在某些方面仍然有待提高。例如,由于 Recordset 固有的 COM 特性,通过网络进行序列化的工作将非常繁重。又如它是二进制对象,所以在不同的平台上运行的模块很难共享它,而且它不能穿过防火墙。另外,Recordset 表示多个记录的单个表。如果该表是由一个或多个 JOIN 产生的,更新原始数据源可能会很困难。如果您要使断开连接的记录集和原始数据源保持协调,数据源必须能够识别 SQL。然而,您的记录集很可能是通过非 SQL 提供程序创建的。
在 ADO.NET 中,ADO Recordset 的所有功能被拆分成几个较简单的对象,DataReader 就是其中之一。DataReader 模拟快速、仅向前的只读游标的操作。
DataTable 是一个表示数据源的简单对象。您可以手动构造 DataTable,也可以通过 DataSet 命令自动填充它。DataTable 不区分它所包含的数据的来源。该对象允许您在内存中处理数据,以及执行浏览、排序、编辑、应用筛选器、创建视图等操作。
ADO 中没有与 DataSet 相对应的对象。DataSet 对象是一个容器类,是实现 ADO.NET 数据抽取的关键对象。DataSet 将一个或多个 DataTable 对象分组。DataTable 通过象行和列这样的通用集合公开它的内容。当您尝试从数据表中读取数据时,您可能会经过两个不同的对象层:DataTableMapping 和 DataView。
DataTableMapping 对象描述了数据源中的数据列和 DataTable 对象之间的映射关系。当填充 DataSet 时,DataSetCommand 对象要使用这个类。它维护数据集中的抽象列和数据源中的物理列之间的链接。
表的视图通过 DataView 对象实现。它表示 DataTable 的自定义视图,可以绑定到特定控件(如 Windows 窗体和 Web 窗体中的数据网格)中。该对象相当于 SQL CREATE VIEW 语句在内存中的实现。
DataSet 中的所有表都可以通过一个公用域放入关系中。这个关系由 DataRelation 对象管理。这看起来很象 ADO 的数据形成,但有一点重要区别。您不需要使用数据形成语言,您最终会拥有一个非常灵活的结构体系。ADO.NET 导航模型使您可以轻而易举地从某一张表内的主行移入它的所有子行。
DataRelation 对象相当于 JOIN 语句在内存中的实现,可用于建立数据类型相同的列的父/子关系。一旦建立了关系,就不允许出现任何会破坏这种关系的更改,如果出现就会导致运行时异常。视图和关系是实现主表/明细表架构的两种方式。要记住,视图只是放在记录上的掩码,而关系是设置在两个表的一个或多个列之间的动态链接。如果使用关系,您不能更改顺序或设置条件。
如果您的代码需要一对一外键关系,并且不更改数据,那么您最好不要使用无格式的 JOIN 命令。如果您需要额外的筛选功能,就应该使用 ADO.NET 自定义视图。