关于 OLE DB 和 .NET 的思考
你我并不相识。不过,面对一个实实在在的问题“软件到底是什么?”,我却没有合适的答案。
设想一下这样一个场景:在一个旅游纪念品商店,你正专注于购买一些没用的东西(主要是纪念品),以便让到机场接你的朋友和亲戚感到开心。这时,往往会有人问你:“第一次来吗?出差还是度假?”
所以,如果你是在做和软件有关的事,而不是在度假,那你就不得不面对这个现实的问题。
那么,软件到底是什么?
回答这种关于存在的问题是很困难的,尤其是如果此时你正在闲逛,背着一背包明信片、考拉图片和袋鼠玩具,包上还印着防鳄鱼的黄色警告标志。
我努力使自己的思维自由而又尽量简单。首先,软件是跟计算机相关的。软件也和演变有关。当然,软件还与数据(特别是数据存储和操作)有关。
回到旅馆后,我仍在思考下面的问题——关于数据的存储和使用,我近年来观察到了怎样的演变?于是,我开始思考 OLE DB 及其在 .NET 方面的演变。
软件进化论
从历史角度来说,ODBC 进行了第一次严肃尝试:它试图创建一种统一的应用程序访问数据库的途径。像软件中的其他东西一样,ODBC 的设计目的是满足某种特定的需要。在信息技术永无止境的进化进程中,它开创了一个新阶段。
ODBC 必须提供一个公用的(最好是抽象的)API,用来访问数据库,而不用考虑数据库的内部细节、语言和表的组织。但是,随着时间的推移,人们发现,面对新的数据驱动应用程序的设计与构造方法,ODBC 越来越无法成功地满足需要。
软件也有自己的进化论。ODBC 以不同的名称、不同的编程模型和新的功能适应了变化,生存了下来,同时又保持了它的真正使命。ODBC 继续以 OLE DB 的名称和功能提供(或多或少地)开放式数据库连接的功能。
OLE DB 作为一种编程接口,将 Microsoft 通用数据访问 (UDA) 策略的理论概念应用于实践。UDA 能够通过基于 COM 的单一编程接口来访问各种类型的数据,包括关系型、非关系型和层次结构型数据。
OLE DB 是作为一种组件技术而设计的,其特点是采用了多层模型。在 COM 桥的一侧是用于保留数据的服务器组件,另一侧则是了解如何连接和请求数据的客户端组件。前者称作 OLE DB 数据提供者;而后者则称作 OLE DB 使用者。
使用者和提供者都是 COM 对象,并能够通过一套 COM 接口互相通信。这种基于 COM 的通信可被概括为在抽象对象(如
DataSource、
Session、
Command 和
Rowset)上执行的操作。因此,当使用者连接到
DataSource,打开
Session,发出
Command,并返回数据
Rowset 时,便会出现这种情况。
ODBC 的这一进化使 UDA 和 OLE DB 添加了一种功能,这种功能就像一个简单的关系表一样,将所有的企业数据粘合在一起,不论它们是关系型、非关系型还是层次结构型。
OLE DB 模型
说到数据访问,我们有两种基本选择。一种是像 UDA 允许的那样,采用通用数据访问策略。另一种则倾向于使用通用数据结构。它强行将现有的所有信息从当前的数据存储区移动到一个能包容所有数据类型的数据库服务器。
使用 OLE DB,需要将客户所有的信息粘合在一起。另一种方式是,强行将客户端升级至新的、更强大的、唯一的 DBMS,而这个 DBMS 能够处理任何格式的信息。
与 ODBC 相比,OLE DB 对数据物理结构的依赖更少。此外,它不必严格基于 SQL。OLE DB 命令可以是 SQL 语句,也可以是其他的一些东西。总的说来,可以将它们看作以任何能够为目标提供者理解的语法写成的文本字符串。
像 ODBC 一样, OLE DB 采用 C++ 的概念进行设计,以尽可能提高中间层模块数据访问的性能。基于同样的原因,OLE DB 不能直接在 Visual Basic® 或 ASP 中使用。
而不计其数的分布式系统却是使用 Visual Basic 来生成组件的。这就是 Microsoft 引入 ActiveX® 数据对象 (ADO) 库的主要原因。
ADO 的编程接口比原始的 OLE DB SDK 更加丰富。虽然在 C++ 应用程序中使用 ADO 是完全可行的,但是 OLE DB 调用经过的代码层次较少,与相应的 ADO 代码相比,能更直接地到达数据。
虽然 ADO 很明显是在 OLE DB 上生成的,但是调用原始 OLE DB 接口和通过 ADO 运行时发出的调用具有不同的相对速度。这一事实导致了语言之间的差异。哪一种更好、更值得推荐呢?是 OLE DB 的 C++ 高性能层次还是 Visual Basic 组件中更简单、更友好的 ADO 模型?
除了提供者和使用者,OLE DB 模型还包括第三个元素——OLE DB 服务。服务是一种 COM 组件,用于处理返回给使用者的“行集”。它就像挂钩一样工作,监督使用者和提供者之间的所有通信。ADO 在很大程度上依赖 OLE DB 服务来添加其扩展功能,如数据塑型、持久性和断开的记录集。
因此,自从人们开始重视构建基于 COM 的分布式应用程序以来,就开发了各种针对某些特定领域的最佳实例。为改进 Web 应用程序的可伸缩性,人们转而使用数据访问断开模型。
简单说来,数据使用者和数据提供者并不总是连接的。一旦建立了连接,便可以发出指定的查询,获取记录并将其放至内存中的存储库,然后从数据源断开连接。然后您再在脱机状态下处理这些记录,并在需要时重新连接或提交更改。这一模型不是在所有情况下都可以使用,不过,一旦它发生作用,您就会发现它在可伸缩性和总体性能方面非常有价值。
许多系统已经进行了转换(或再转换),通过客户端游标服务来部署 ADO 记录集,从而启用数据断开。OLE DB 还不是专用于此类交互的模型,所以 ADO 是通过中间 OLE DB 服务进行扩展的。
由于其结构所固有的灵活性,OLE DB 可以成功地应用于断开连接方案,但是,这当然不代表最佳的工作方式。这一实现方案的另一小小的限制是:方案较多地依赖 ADO 记录集进行工作,以至于人们怀疑它不可能总是把每件事都做好。这样的对象如何才能在各种情况下成为最快的工作工具,不论是连接还是断开,有没有 XML,是创建的还是从磁盘加载的?
此外,考虑到 ADO 的功能包与原始 OLE DB SDK 显著不同,使用 OLE DB 将导致明显的不一致现象。
因此,ADO.NET 成为数据访问技术进化中的下一步骤。不过,从名称上看来,ADO.NET 似乎只是 ADO 的继承者。.NET 中的 OLE DB 又是怎样的?
.NET 托管提供者
永恒的进化论规律现在将 OLE DB 技术向前推进了一步,以满足新用户的要求。在 .NET 中,Web 应用程序首先是一个断开的应用程序,它利用新设计的特殊工具来管理数据。
.NET 框架使得类能够处理数据。这些类——特别是 ADO.NET 和 XML 名称空间——可供收集、读取和写入。ADO.NET 和 XML 子系统最终取代了 ADO 和 OLE DB SDK。现在,您拥有了一个唯一的、以语言为中心的方法来获取和设置数据。
ADO.NET 类对数据源的抽象能力甚至比 ADO 还要好,因为它明确设计为以数据为中心,而 ADO 中仍然使用以数据库为中心的设计。
.NET 中对应于 OLE DB 数据提供者的部分称为“托管提供者”。它们的作用如下图所示。
图 1:托管提供者层次结构图在 OLE DB 中可以按照相似性来识别两个交互的层,即我上面提到的托管使用者层和托管提供者层。在处理数据时,.NET 应用程序不必将特殊的类或组件作为使用者模块使用。
如果 .NET 应用程序只使用本机框架中的
DataSet 或
DataReader 对象,则其将立即成为“托管”数据使用者。要真正地获取数据,应使用从
DataSetCommand 和
DBCommand 中继承的特殊类的实例。这些类代表了到数据源的链接。
你只需使用了解如何处理给定提供者的导出类,而不用指导通用对象来处理给定的提供者。所以出现的情况是,
SQLDataSetCommand 将处理 SQL Server 数据库,而
ADODataSetCommand 将包装所有现有 OLE DB 提供者。
托管提供者将隐藏在这样的 DataSetCommand 类中。你永远不会意识到它们的存在,也无需特意了解它们。只要使用类和设置属性就可以了,这让人感到愉快。
在这种情况下,上图中的托管提供者层使用的交互模块与 OLE D