关于 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 的通信进行归类。因此,实际情况是,使用者连接到某个数据源,打开一个会话,发出一个命令,并提取数据的行集合。 ODBC 的进化过程导致 UDA 和 OLE DB 提高了将所有企业数据融合在一起的能力,几乎就像是一个关系表一样,而不考虑其关系型、非关系型,甚至是层次结构型的特性。

OLE DB 模型

当谈到数据访问时,您有两个基本选择。一个是统一数据访问策略,正如 UDA 允许您做的一样。另一个是统一数据结构。它迫使您将您可能拥有的每一位信息从其当前的数据存储设备移到单个综合数据库服务器。 通过使用 OLE DB,您可以试图将客户端目前需要的所有内容融合在一起。如果换另一个方法,您可以强制客户端升级到更强大的、唯一的新 DBMS,DBMS 有能力处理您所需的任何格式的信息。 与 ODBC 相比,OLE DB 与数据的物理结构的相关性要小得多。此外,它并不是严格基于 SQL。OLE DB 命令可以是 SQL 语句,但也可以是其他内容。通常,可以将它们看作是按照目标提供程序可以理解的任何语法编写的文本字符串。 与 ODBC 类似,在设计 OLE DB 时充分考虑了 C++,以便最大程度地提高中间层模块中数据访问的性能。由于这些相同的原因,在 Visual Basic 或 ASP 中不能直接使用 OLE DB。 而无数的分布式系统应该使用 Visual Basic 来生成组件。这就是为什么 Microsoft 推出了 ActiveX 数据对象 (ADO) 库的主要原因。 与原始 OLE DB SDK 相比,ADO 具有更加丰富的编程接口。毋庸置疑,可以在 C++ 应用程序中使用 ADO,尽管如此,但与相应的 ADO 代码相比,OLE DB 调用经过较少的代码层,并且可以更直接地获取数据。 虽然 ADO 明确建立在 OLE DB 之上,但对原始 OLE DB 接口的调用和通过 ADO 运行库发出的调用具有不同的相对速度。这一事实引发了一种基于语言的分化。哪个更好,更建议使用哪一个呢?是选择 OLE DB 的 C++ 高性能级别,还是选择 Visual Basic 组件中更方便、更宽松的 ADO 模型? 除提供程序和使用者以外,OLE DB 模型还包含第三个元素,即 OLE DB 服务。服务是一个 COM 组件,用于处理使用者返回的行集合。其工作方式就如同一种挂钩一样,用于监视使用者和提供程序之间的所有通信。ADO 主要依靠 OLE DB 服务来添加其扩展功能,例如,数据成形、持久性和断开连接的记录集。 随着人们越来越重视生成基于 COM 的分布式应用程序,很多针对特定领域的最佳做法已经制订出来了。为了提高 Web 应用程序的可伸缩性,人们转而使用数据访问断开连接模型。 在 nutshell 中,数据使用者和数据提供程序之间并不始终保持连接。在建立连接后,您发出给定查询,从内存中的库中提取记录,并断开与数据源的连接。您以脱机方式处理这些记录,如果需要的话,随后再重新连接并提交您的更改。此模型并不适用于所有人。但在其适用的场合中,它在提高可伸缩性和总体性能方面具有非常高的价值。 很多系统已经通过客户端游标服务进行(重新)转换以使用 ADO 记录集,此服务支持数据断开连接。人们并没有明确将 OLE DB 视为此类交互模型,因此,通过中间 OLE DB 服务来扩展 ADO。 OLE DB 的结构具有内在的灵活性,因此,在断开连接的情况下可以成功地使用 OLE DB,但这显然并不代表最佳的工作方式。这种实现方式的另一个微妙的限制是,很多工作都是依赖 ADO 记录集来完成的,因而,人们不免会怀疑它们并不总能有效地完成工作。如何才能使此类对象在连接和断开连接的情况下、有和没有 XML 的情况下以及进行构造或从磁盘加载的情况下工作时都能具有最快的速度呢? 此外,如果您考虑到 ADO 功能集与原始 OLE DB SDK 具有非常大的差异,那么,您就会知道它与 OLE DB 具有很大的不一致性。 因此,ADO.NET 成为数据访问技术进化过程的下一个阶段。从名称上看,ADO.NET 似乎只是 ADO 的后续版本。OLE DB 在 .NET 中又是什么情况呢?

.NET 托管提供程序

按照达尔文进化论的永恒法则来看,现在 OLE DB 技术必须向前迈进一步以满足新用户的需求。在 .NET 中,Web 应用程序主要是断开连接的应用程序,该应用程序使用新设计的特别工具来管理数据。 .NET 框架提供了处理数据的类。这些类(特别是 ADO.NET 和 XML 命名空间)提供了收集、读取和写入功能。ADO.NET 和 XML 子系统最终取代了 ADO 和 OLE DB SDK,因此,现在使用一种与语言无关的方法来获取或设置数据。 ADO.NET 类在提取数据源方面甚至比 ADO 做得更好,这是因为它具有以数据为中心的明确设计,与此相反,ADO 却仍然使用以数据库为中心的模型。 OLE DB 提供程序的 .NET 副本称为托管提供程序。下面的图中说明了它们的角色。

图 1. 托管提供程序的结构示意图

正如在 OLE DB 中一样,您可以识别两种交互的层,为了保持谐音,我将它们称为托管使用者层和托管提供程序层。要处理数据,.NET 应用程序不需要挑出特殊的类或组件来作为使用者模块。 .NET 应用程序只使用原始框架中的 DataSetDataReader 对象,并立即成为“托管”数据使用者。要物理地提取数据,您应该使用从 DataSetCommandDBCommand 继承的特殊类的实例。这些类表示到数据源的链接。 无需指示更常规的对象来处理给定提供程序,您只需使用已知道如何处理该给定提供程序的派生类即可。因此,实际情况是,SQLDataSetCommand 处理 SQL Server 数据库,而 ADODataSetCommand 包装所有现有的 OLE DB 提供程序。 托管提供程序就隐藏在这些 DataSetCommand 类当中。您不会意识到它们的存在,并且从不需要明确知道它们。您使用类并设置属性,然后就万事大吉了。 从内部来看,上图中的托管提供程序层使用的交互模型与 OLE DB(甚至更早的 ODBC)中的交互模型并没有多大差异。使用者命令类针对的是包装数据源的特定组件。它知道发出的用于读取和写入源上的行的协议。它还以 .NET 类完全知道如何处理的格式返回结果。 为了便于理解,让我们看一些在 OLE DB 和 .NET 中检索数据时都会用到的元素。

 

OLE DB 提供程序

.NET 托管提供程序

标识

COM progID

包装在命令类中

获得结果的方式

通过行集合或 ADO 记录集

通过 DataSet 或 DataReader 类

更新方式

通过提供程序特定的命令

通过提供程序特定的命令

传输格式

二进制文件

XML

表 1. 比较 OLE DB 和 .NET 数据提供程序

在 OLE DB 中,目标提供程序是通过其 COM progID 标识的。而在 .NET 中,此类细节隐藏在访问器类中。 OLE DB 提供程序始终返回行集合;COM 对象主要公开 IRowset 接口。如果通过 ADO 访问数据,则将行集合转换为更丰富且可编写脚本的对象(称为“记录集”)。 .NET 应用程序对不同的功能使用不同的类。DataReader 类是一种简单、快速的只进游标,它在连接方式下工作并针对每条记录提供访问。在完成此过程后,必须显式断开连接。相比之下,DataSet 对象是一个内存中的断开连接的表集合。其填充的内容源于 DataSetCommand 类。DataSet 对象的内容基于 DataSetCommand 类从数据源返回的 XML 流。 我将在后面几期中讨论 DataReaderDataSet 类。 数据以二进制格式从提供程序传输到使用者,如果您使用 OLE DB 的话,还要通过 COM 封送处理方可传输。而在 .NET 中,托管提供程序返回 XML 流。 这两种提供程序均支持查询语言,通常为带有供应商特定的专用扩展的 SQL。通过这种语言,您可以执行更新并询问数据源。 那么,OLE DB 和 .NET 数据提供程序有什么差异呢?抽象地说,它们使用相同版本的数据访问。但托管提供程序要简单和专用得多。它们可提供更好的性能,原因主要有以下两个:首先,托管提供程序并不使用 COM Interop 桥梁来获取和设置数据。而在这一点上,作为 COM 组件,OLE DB 提供程序则别无他选。再者,托管提供程序通常利用供应商对数据源内部情况的了解,以快得多的速度获取和设置行。这也正是 OLE DB 提供程序所具有的功能;但在 .NET 中使用时,OLE DB 提供程序也为其基于 COM 的特性付出了相应的代价,它们需要编写额外的代码来将数据转换为 .NET 特定的类。

现有的托管提供程序

从 Beta 1 起,.NET 框架提供两个托管提供程序:一个用于 SQL Server(版本 7.0 和更高版本),一个用于您能通过 OLE DB 提供程序访问的所有数据源。 SQL Server 托管提供程序隐藏在特定的类中,例如,SQLDataReaderSQLDataSetCommandSQLCommand。这些类利用直接访问来访问低级 SQL Server 文件系统。下图给出了提供程序的类示意图,它将以前的常规架构映射到 SQL Server 的托管提供程序。

图 2. SQL Server 托管提供程序的类示意图

OLE DB 托管提供程序在 .NET 中的作用与 ODBC 的 OLE DB 提供程序在 Windows DNA 系统中的作用相同。基本上,它代表了向后兼容性,并生动地说明了任何 .NET 应用程序都可以处理 OLE DB 支持的任何现有数据源。OLE DB 托管提供程序的类示意图如下所示。

图 3. OLE DB 托管提供程序的类示意图

注意,在 Beta 2 中,应该将 ADOxxx 类重命名为 OleDbxxx。 OLE DB 托管提供程序向调用者公开 .NET 类,但利用指定的 OLE DB 提供程序来获取行。.NET 应用程序和基础 OLE DB 提供程序(COM 对象)之间的通信是通过 COM Interop 桥梁进行的。 通常,在 .NET 中,您可以通过这两种提供程序访问 SQL Server 7.0(和更高版本)表。SQL Server 托管提供程序直接访问 DBMS 文件系统以请求数据。而 OLE DB 托管提供程序依靠 SQL OLE DB 提供程序的服务,因此需要穿过一个额外的代码层。 目前,如果您的任何目标数据源不是 SQL Server,则 OLE DB 托管提供程序是您必须使用的唯一方法。通过此相同的通道,您还可以访问任何 ODBC 数据源。 OLE DB 托管提供程序是建立在 COM Interop 桥梁上的瘦包装类,它调用本机 OLE DB 提供程序。除设置和终止调用以外,此类模块还负责将返回的行集合包装到 DataSetADODataReader 对象中,以使 .NET 能够进行进一步的处理。 在 .NET 代码级别,通过本机托管提供程序或 OLE DB 提供程序访问 SQL Server 表实质上就是更改相关类的前缀。以下是 SQL Server 代码:

Dim strConn, strCmd As String strConn = "DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;" strCmd = "SELECT * FROM Employees" Dim oCMD As New SQLDataSetCommand(strCmd, strConn) Dim oDS As New DataSet oCMD.FillDataSet(oDS, "EmployeesList")

以下是 OLE DB 提供程序代码(不同之处以粗体表示):

Dim strConn, strCmd As String strConn = "Provider=SQLOLEDB;" strConn += "DATABASE=Northwind;SERVER=localhost;UID=sa;PWD=;" strCmd = "SELECT * FROM Employees" Dim oCMD As New ADODataSetCommand(strCmd, strConn) Dim oDS As New DataSet oCMD.FillDataSet(oDS, "EmployeesList")

正如您所看到的一样,表面上看差别非常小;只是连接字符串和命令类而已。但使用一个类或另一个类会产生非常大的差异。

有关 OLE DB 的存在主义问题

.NET 托管提供程序代表了数据访问技术进化过程的下一阶段,但从 Beta 1 起,不再有用于编写数据源特定的托管提供程序的记录 SDK。等到发行 Beta 2 时,仍无法回避有关 OLE DB 和 .NET 的一些基本问题。 为 OLE DB 开发的所有代码都是遗留代码吗?公司在为其自己的数据编写提供程序所做的(并且通常正在做的)所有工作会带来什么回报? 请相信,OLE DB 并不是一种淘汰的技术。它仍然是功能丰富的、通用的、与 .NET 无关的编程接口的基本规范。它并不是 .NET 所特有的,但它于 .NET 上得到了很好的支持。 也就是说,如果您要公开自定义数据,则不能忽视 .NET 和托管提供程序的出现。那么,您的数据提供程序应配备的最佳接口是什么呢?您打算如何立即(例如,从下星期一早晨 8 点开始)公开您的数据呢? .NET 利用开放的标准,并在很大程度上基于 XML。考虑到这一点,如果您要公开基于文本的专用数据,只需考虑使用 XML 发布它即可(可能使用自定义架构)。.NET 中有很多用于处理 XML 数据的工具,所以提供一个包装类根本不成问题。 对于更复杂的数据存储,OLE DB 提供程序仍然有效,因为您需要面向更大的用户群,他们可能并不局限于 .NET。对于 .NET 特定的应用程序,托管提供程序的确可以提供非常大的性能优势,但在这种情况下我会非常谨慎的,尤其是在星期一。不要忘了,目前尚未发布托管提供程序的 SDK,但 Microsoft 承诺提供托管提供程序 SDK。 因此,总而言之,我应该在本星期一早晨开始编写的下一个数据提供程序将包含一对 OLE DB 提供程序以及一个用 XML 编写的 .NET 包装类。我的第一个选择并不是通过 COM Interop 将 .NET 类包装在 OLE DB 提供程序的外围。我倾向于使用相同的源代码(已进行了一定的改编)。在这种情况下,使用托管 C++ 语言可能是便于重复使用“物理”代码的最好方法。

OLE DB 的归宿

从现在起,一些年后我这里所做的预测将会得到印证。从长远看,我大胆地预测 OLE DB 将与 SGML 的结局类似(OLE DB 相对更加凄惨),SGML(标准通用标记语言)是 XML 的前身。 SGML 的出现是为了解决数据交换问题,但其却从未成为实际的标准,这可能是因为它功能太强,太复杂,而不便于日常使用。实际情况是,它的创造性原则只有在正确地进行范围缩小以及专门化以产生 XML 后,已被广泛地采用了。 我的预测是,在 .NET 站稳脚跟后,OLE DB 的重要性就会逐渐降低,直至消失。不知道这一过程实际要持续多长时间,但我相信它必定要经历这一消亡过程。 不久后,我将给出引用的材料。请继续关注我们的工作。

对话栏:的确,我现在感觉被遗弃了

您可能生活在另一个时空!您怎能将现有的 ADO 代码(多数情况下是在六个月前后编写的)定义为遗留代码呢?.NET 尚未到达其 beta 计划的第二阶段,您又怎能借技术/平台 .NET 的名义进行定义呢?

要是不稍稍夸张一点,生活岂非平淡无奇?不管怎样说,您说得非常对。 我们真的可以将最近在很多 DNA 系统中发现的所有 ADO 代码化石定义为遗留代码吗?我的回答仍然是“是的,我们应该这样做”。但我知道它听起来确实让人感到泄气。 我认为“遗留代码”就是与宿主平台核心不再保持一致的代码。相信我,这就是 .NET 中即将出现的情况。当然,一定会有办法在 .NET 中集成现有代码、组件和应用程序。 .NET 并非彻底性的变革,在今后几年中,它将吸收 Windows 中软件的任何有机成分。抗拒这种趋势是徒劳的,您必将顺应这种潮流。无论代码的年龄有多长,按照我对遗留代码的定义,真正重要的是源和运行库之间的一致性。 .NET 改变了 Windows 运行库,使之成为托管运行库。虽然 COM 和 Windows SDK 没有被淘汰,但您必须按照另一种模型来编写代码。不管这种新运行库的基础是什么,您必须遵循一种特殊的新模型。而此模型将是未来的 Windows 模型。 Windows 并没有被淘汰,但它将会发生变化。COM 并没有被淘汰,但它必须采用 .NET 类的形式。ADO 并没有被淘汰,它仍然有效,但 ADO.NET 的 .NET 功能才是 ADO 的未来。 .NET 并不只是 Windows 6.0,ADO.NET 也不是人们可能称为 ADO 3.0 的产品的别致的新名称。它是完全不同的,并且涉及到产品的方方面面。它是一个新平台。为方便起见,它或者是另一个平台,或者是遗留代码(集成后)。 遗留代码没有年龄限制。我知道人们在本周、六个月后甚至在 .NET 实际发行后,仍会在编写 DNA 系统。我并不是说这样做一定是错误的,或者应该绝对避免。我只是想让您知道您是在逆流而上。


 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章