荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: playboy (不谈爱情), 信区: Program
标  题: [转载] 客户/服务器应用程序开发指南
发信站: BBS 荔园晨风站 (Mon Apr 17 14:26:37 2000), 转信

【 以下文字转载自 Internet 讨论区 】
【 原文由 williamlong 所发表 】
《客户/服务器应用程序开发指南》详细介绍了 Visual Basic 企业版提供的开发工
具,并且为使用这些工具来构造三层客户/服务器应用程序的开发者提供了一套体系结
构设计思想。这里的应用程序模型将应用程序划分为逻辑上的服务组,而服务组通过跨
越企业网络作为独立物理部件来分别实现。物理部件具有可共享、可扩展、可重定位等
特点。
本书的每一部分分别说明如何使用 Visual Basic 企业版所提供的丰富工具和策略,来
设计并实现基于部件的客户/服务器应用程序的某个方面。从建立抽象的体系结构开
始,然后介绍详细设计和部件部署,最后涉及与企业数据源的接口。

章节

 第一章 概述介绍用于分布式应用程序设计的三层模型,以及基于部件的设计所需要使
用的工具和技术。此外,还为工程管理者提供了一系列新的开发策略,利用它们可以更
好地构造分布式的应用程序。
 第二章 设计指南为构造基于部件的解决方案提供了详细的设计指南。其中包括:概念
设计、逻辑设计和物理设计;如何实现 ActiveX 部件(即以前的 OLE 服务器)用于分
布式的部署;考查设计良好的部件应用程序的完整工作模型。
 第三章 数据访问选项概述通过 Visual Basic 企业版进行客户/服务器模式数据访问
的不同方法:数据访问对象 (DAO) 和远程数据对象 (RDO)、RemoteData 控件 (RDC)、
开放数据库互连 (ODBC) 应用程序编程接口以及 Microsoft SQL Server 的 VBSQL 接
口。

第一章概述
Microsoft Visual Basic version 5.0 企业版为群体开发者提供了开发、测试和使用
大型分布式客户/服务器应用程序所需的编程环境和集成工具。《客户/服务器应用程序
开发指南》则介绍了一种用于开发这种应用程序的体系结构和设计框架。
有了这些工具和技术,就可以利用网络上那些可共享的、可重用的和可重定位的部件
“搭积木”,轻松地完成应用程序的开发。
概述部分简要介绍分布式应用程序设计的三层模型。这种模型与所用的工具和技术结
合,可以获得基于部件的设计方法的全部好处。这种模型还为开发策略提供了一整套新
思想,项目管理者可以用它来处理分布式应用程序开发中的新问题。
第一节 前   言
概要介绍将应用程序划分为既独立又相互配合的部件的好处,这里,划分好的部件应该
可以独立地定位在网络上。同时简要介绍企业版中关于基于部件的设计方法的有关特
性。

企业的客户/服务器计算模型正在发生日新月异的变化。今天的企业面对着来自全球的
竞争,需要采用新的业务方法,需要使自己的技术保持领先,为此必须建立良好的信息
管理系统,这直接关系到企业的兴衰。
企业版的 MicrosoftVisual Basic5.0 为企业开发者提供了一套先进的工具包,辅之以
抽象的设计战略和具体的实现指南。使用可重用、可重定位的软件部件,建造出强健
的、可扩展的分布式应用程序。
新的客户/服务器开发模型的关键概念是三层结构。这种模型将应用程序的任务在逻辑
上分为三个服务“层”,业务规则、数据访问和分布式的处理任务等都可封装到部件之
中。部件具有可共享、可重用的优点,根据性能、维护等方面的具体要求,从物理上来
说,可将部件部署在网络上的任何地点。这种多方面的适应性能够最大程度地迎接变革
的挑战。
主题

一. 基于部件的应用程序介绍将应用程序划分为可共享、可重用、可重定位的部件的
优点。

在企业版中,可使用三层结构的客户/服务器开发模型对应用程序进行任务划分。在这
一过程中,不同的项目组各自开发独立的部件,这有助于提高生产效率。然后将这些部
件部署在物理网络的多个计算系统上。
可用各种独立于语言的工具创建这些部件,也可从第三方购买。
这种基于部件的应用程序开发策略可以带来以下的好处:
■ 可重用性。许多应用程序可共享和重用封装在部件中的功能。
■ 灵活性。从桌面计算环境到功能更强的网络服务器,随处都可分配工作,这有利于
协调性能和网络带宽。
■ 可管理性。将大型复杂的工程细分为简单、安全的部件工程。
■ 易维护性。将业务逻辑部署在中央服务器上,而不是分散在用户桌面上,这有助于
处理各种变化,并缩短解决方案的往返时间。

面向用户服务、面向业务服务和面向数据服务以及在服务之间协商的业务逻辑都包含在
彼此独立的对象中,且与它们在网络上的位置无关,而对象之间可相互进行操作。通过
这种方式,系统以及系统所支持的企业将是灵活的和可扩展的,从而能够满足不断发展
的业务需求。

二. 使用 Visual Basic 进行企业开发介绍企业版中为支持三层客户/服务器模型的开
发而提供的工具和策略。

企业版的设计有助于将应用程序划分为分离的部件,并灵活地部署这些部件,且有助于
在逻辑上将应用程序的需求分为三种服务:
■ 用户服务
■ 业务服务
■ 数据服务

本书将介绍这种三层的逻辑应用程序模型,在这种模型中定义了应用程序的功能,并将
功能作为这些服务和彼此协作的部件的集合,这些部件共同工作以期满足特定需求。通
过这些集合,部件可以互相独立地提供用户、业务、数据服务。
例如,可用这种方法管理产品、顾客和收入数据,这三种数据是企业应用程序的中心,
并由变动频繁的规则定义。在三层模型中,规则可能在部件之中,而部件使用取自数据
库的信息进行计算,并将结果返回到用户的桌面上。部件为一个概念层,专门用来运用
业务规则并与编程逻辑分离开,编程逻辑负责处理数据访问和用户交互任务。
可从客户机访问的封装部件部署在服务器上,它独立于数据和用户而进行操作,用户看
不到其工作过程。这种根据要素之间关系的划分,分别提供用户、业务和数据服务。如
果规则发生了改变,这种划分将减轻维护的工作量,只需要维护那些发生变化的部件。

 三.分布式的 ActiveX 部件说明 Visual Basic 5.0 版的功能,利用这些功能构造并
部署 ActiveX 部件。
将部件部署在网络上可以使大部分企业应用程序的功能更强。Microsoft 的分布式计算
平台包括一系列的设计与接口标准,即所谓的 ActiveX。ActiveX 标准是对以前的 OLE
自动化技术的扩展。在企业版中,同一计算机上的 ActiveX 部件可以通过 COM
(Component Object Model)接口相互通讯,而不同计算机上的部件也通过同样的 COM
进行通讯。在网络上进行通讯的时候,ActiveX 部件能够通过两种远程机制响应客户端
的请求:分布式的 COM,用于 Microsoft Windows 和 Windows NT等 32 位平台;远程
自动化,用于 16 位的计算平台上。
这些远程平台的主要优点是:独立于具体的物理部件。就是说,使用 Visual Basic 创
建 ActiveX 部件时,不必预先知道如何来部署它。同样的部件可以位于本地的计算机
上,也可以被重定位到使用分布式 COM 或远程自动化的远程计算机上,这在设计上提
供了相当大的灵活性。另外,还可以对部署策略进行适当的修改,而不需要重新编译部
件。
ActiveX 标准非常适合为企业级的客户/服务器系统建造分离的部件,理由如下:
■ 因为分布式的 COM 和远程自动化技术都提供了与本地的自动化相同的编程接口,所
以将应用程序进行划分的技术与开发本地执行的应用程序相似。而且可以很容易从任何
客户端访问 ActiveX 部件。
■ 同 SQL 相比,使用 Visual Basic 语言可以更直接地描述复杂业务规则算法,并加
以注释和维护。Visual Basic 还为源代码的测试、调试和控制提供了更好的环境。
■ ActiveX 部件可以是自行开发的(实现公司专门的业务规则),也可以是从第三方
购买的(用于一般性的服务)。
■ 由于可以部署在服务器上,ActiveX 部件可以省去桌面上的大量计算任务。
■ 无论本地执行还是远程执行,部件之间的二进制通讯接口是相同的,所以可将部件
在不同的机器上移动而无需对部件或其客户应用程序重新进行编译。

ActiveX 部件能够为 Visual Basic 开发者提供更多的方便、更强的功能、更大的灵活
性,因此,在创建企业级的客户/服务器系统应用程序时,ActiveX 部件是基本的工
具。

第二节 服务模型

描述了一种方法,把应用程序看成提供和接受服务业务对象的集合。这种方法提供一种
概念框架。在此基础上,可开发出三层客户/服务器应用程序。

要充分利用好 Microsoft Visual Basic 企业版中提供的工具和策略,关键在于透彻地
理解三层结构方案的客户/服务器模型。作为一种先进的协同应用程序开发模型,这种
方案将客户/服务器系统中各种各样的部件划分为三“层”服务,它们共同组成一个应
用程序:
■ 用户服务
■ 业务服务和其它的“中间层”服务
■ 数据服务

这些层并不一定与网络上的具体物理位置相对应。相反,它们只是概念上的层,借助这
些概念可以开发出健壮的、基于部件的应用程序。如果使用这种方法设计应用程序,开
发人员在网络上部署进程及数据时可以有相当大的灵活性,从而有利于实现最佳的性
能、更好的安全性以及更方便的维护。
我们将这种三层的应用程序设计模型称为“服务模型”。
主题

 一.基于部件的应用程序开发服务模型提供了一种新的应用程序开发方法,它是基于
若干自包含的、可重用的、可重定位的部件的集成。

使用服务模型,可以把应用程序的需求分解成明确定义的服务。在定义了服务之后,需
要进一步创建具体的物理部件来实现它们。根据性能和维护的需求、工作量、网络带宽
以及其它的因素,可以在网络上灵活地部署这些部件。
这种基于部件的解决方案的方法将应用程序的开发分成两类大的任务。一类任务是开发
能被很多程序使用的核心部件(ActiveX 控件和服务器、存储过程等等)。另一类任务
是集成这些核心部件提供的服务,构造出特定的业务解决方案。
与传统的单片应用程序开发方法相比,基于部件的方法具有以下优点:
■ 开发人员可以专门做自己最擅长的工作。培训与再培训的费用将因此而显著地减
少。
■ 能够使用 Visual Basic、C/C++、SQL 或其它工具编写可重用部件。这在很大程度
上提供了程序语言和代理商的独立性,允许部件开发者使用最适合特定任务的语言和工
具。
■ 部件可以部署在网络上,从而取得效率、性能、安全和维护上的最大利益。可以将
一个应用程序的某些部件驻留在中央数据库服务器上,某些部署在部门性的“业务”服
务器上,另外的部分驻留在最终用户的客户机上。在设计功能强大、需要良好协调的若
干应用程序时,开发人员可以根据网络以及基础设施的实际情况进行部署。部件的实际
位置对最终用户是透明的。
■ 可重用性是指“创建能够被很多应用程序使用的通用部件”,这将减少开发的开
销。部件的使用者只需要理解向他们公开的接口,而不需要知道部件的内部结构和部件
使用的数据。这样就能够以最少的开销开发尽可能多的、高质量的应用程序。这也有助
于实现应用程序之间的高度一致性、兼容性和业务完整性。

部件方案的种种优点使它成为客户/服务器应用程序开发方法中的佼佼者。然而,要获
得这种方法所具有的好处,需要细心地策划和设计。通常,设计过程包括以下步骤:
1. 确定应用程序的需求,即概念设计。
2. 把这些需求映射成抽象的业务对象(例如客户清单或会计帐簿)以及它们提供的服
务(例如生成结帐语句),这被称为逻辑设计。
3. 把业务对象及其服务映射为软件部件,即物理设计。
4. 决定部件在网络上的分布方式,即部署。

设计过程的每一步都需要经过反复调整。例如,在确定应用程序的需求时,设计者首先
调查最终用户和业务经理,写出叙述性的用户使用脚本。将该说明书发回用户,以便进
行核对并获得反馈信息。这个过程可能需要重复进行好几次,每一次都使得需求说明更
加详细和精确。下一步,这个叙述性的用户说明书需要翻译成形式化的描述,分解为业
务对象和服务,然后该文档需要再次进行优化。

二.理解服务模型服务模型将应用程序的需求划分为三个大的服务领域,为设计可扩展
的分布式应用程序提供了结构框架。

服务是一些相关功能的集合,这些功能可以响应操作请求和信息请求,请求必须遵守公
开的接口和规范,而且只能通过一致的接口进行访问,但是具体实现被封装起来了,对
外部是不可见的。
服务模型将应用程序看作用来满足客户需求的一组功能或服务。它鼓励开发人员把应用
程序划分为服务和功能的集合,开发出的功能模块能够被重用、被多个应用程序共享,
并能在网络上进行分布式的部署。
按照这种观点,服务的规格说明是服务的提供者与使用者之间达成的协议。服务是通过
接口来定义的。它允许提供者将具体的实现细节封装起来。正是这种协议使重用成为可
能,并且有利于在企业范围内实现一致的软件规范。
以 Microsoft 为例,字处理应用程序与电子表格应用程序可以共享同一个数据访问部
件,这样应用程序可以不知道数据的实际存储方式和存储地点。为了管理表格性质的信
息,字处理程序可以调用电子表格的服务。通过使用 ActiveX 控件、对象和其它的可
重用部件,许多服务对应用程序开发者是“立即可用的”。可以自己开发提供这些服务
的部件,也可以购买其它供应商的产品。
服务模型并不局限于某个特殊的产品包、某种技术或某个供应商。Visual Basic 提供
了许多的工具和技术来开发基于服务的应用程序,它也可以很方便地与其它工具结合。
集体开发者能够在其组织内现有的或计划中的企业结构的基础上开发基于服务的部件解
决方案。


在典型的业务应用中,有三种服务类型。如下表所示,它们各自有各自的属性:
服务类型 服务的属性

用户服务 提供信息和功能、浏览定位、保护用户界面的一致性和完整性
业务服务 共享的业务政策,从数据中生成业务信息,保护业务的一致性
数据服务 数据的定义,永久数据的存储和检索,数据一致性的保护

除了定义应用程序中每一种服务的功能之外,上面的分类还为进行结构化开发的开发小
组和开发过程提供了一种组织线索。在“开发策略”中讨论了开发组和开发过程模型。
注意   值得注意的是,有一些“中间层”服务不能很明确地归为三类之一。例如,作
为应用程序支持服务的图形处理、纯粹的数字计算以及安全服务等通常既不属于用户服
务和数据服务,也不是严格的业务服务。

各种服务通过网络联系在一起并且相互合作,从而支持一个或多个业务进程。在这种模
型下,应用程序是用户、业务以及满足一定需求的数据服务的集合体。
服务总是被设计成通用的,并且遵守公开的接口标准,所以它们可以被重用,并能被多
个应用程序所共享。
用户服务
用户服务提供了一个可视化的接口,用来表示信息和收集数据。它们确保业务服务能够
提供所需的业务处理能力,并且使用户与应用程序紧密结合起来,以处理某项业务。
用户服务通常表现为用户界面,而且通常位于最终用户工作站上的一个可执行程序中。
然而,有时候几个服务可能位于几个分离的部件之中。例如,某种数据表示方式需要使
用可视化的网格显示数据,而网格位于另外的 ActiveX 控件中。
业务服务和其它的“中间层”服务
业务服务是联系用户服务和数据服务的“桥梁”。它们响应用户(或其它的业务服务)
发来的请求,执行某种业务任务。为了完成这些任务,它们对相应的数据运用形式化的
过程和业务规则。如果所需的数据驻留在数据库服务器上,通过它们可以获得所需的数
据服务,或运用需要的业务规则。用户不能够直接与数据库打交道。
业务任务是由应用程序的需求定义的一种操作,例如添加一个定货单或打印客户列表。
业务规则是控制业务工作流程的策略。例如,在生成客户发票的时候,需要在库存价值
的基础上进行一定幅度的上调,这就是业务规则。
与任务相比,业务规则更容易发生改变。所以在实现时最好将业务规则封装在单独的部
件中,与应用程序的逻辑分隔开。
例如,某个应用程序需要“得到销售价格”,这项服务可以被封装在中央网络服务器上
的一个部件中,应用程序可以访问它。如果计算过程发生了改变,仅需要在一个地方进
行修改就可以了,应用程序本身是不需要修改的。一旦业务服务改变了,所有的“得到
销售价格”请求都将使用已修改的业务服务得到新的计算结果。
需要再次说明的是,某些中间层服务严格地讲不能被视为业务服务。例如,登录屏幕用
来提示用户输入密码以获得网络访问权,它实际上是与中央数据库服务器连接的自动部
件。图形处理程序经常会把计算量大的图形处理任务转移到有更多 CPU 处理能力的部
门级服务器上进行。
数据服务
数据服务包括数据的定义、维护、访问和更新,以及管理并响应业务服务的数据请求。
与其它种类的服务相比,这种类型的服务已经被研究了很长时间,所以就好理解得多。
它们的物理实现可以在某种数据库管理系统 (DBMS) 上,也可以是一个异种数据库的集
合,这些数据库可能驻留在多种平台上,运行在服务器和大型计算机上。
将数据服务与应用程序的其它部件分离开,在维护、修改甚至重构数据结构及访问机制
时,可以丝毫不影响业务服务和用户服务。在“数据访问选项”中讨论了数据服务的物
理实现。

三.服务模型与分层模型服务模型使用逻辑上的三层结构,这不同于需要网络的三个物
理层的部署模型。
 在日益增长的分布式计算文献中,三层结构模型有时会与其它各种“分层”模型相混
淆,后者为每一种服务划分出一个物理层和一个逻辑层。
这种物理意义上的分层模型需要严格的交互结构(层到层),而这在应用中常常是不必
要的。另外,分层模型还隐含了一种部署结构,每一层对应于一种计算平台:数据服务
在数据库服务器上,业务服务在一个或多个业务服务器上,用户服务在桌面工作站上。
有两种流行的分层模型:
■ 两层模型鼓励进行任务划分,但是其技术平台仅包含客户桌面和后端服务器,因而
受到了限制。
■ 三层模型准确地标识了三种基本类型的服务。但是,要求它们之间的关系是非常结
构化的,必须从用户层到业务层,再从业务层到数据层,反之亦然。

真正的服务模型强调的是逻辑结构(概念意义上的),而不是物理结构(部署意义上
的),因此上面的模型都不是真正的服务模型。与分层模型相反,服务模型允许服务在
物理上驻留在任何地方。任何服务都可以根据特定的功能需求激发任何其它的服务。同
其它模型提供的物理分层方法相比,服务模型提供了更高的灵活性和可重用性。
服务是逻辑意义上的,而非物理意义上的
服务模型描述了应用程序的概念结构。它强调的是逻辑意义而不是物理意义。它说明如
何设计应用程序,而不是如何具体部署。
在开发的实现阶段,各种服务之间的区别可能是比较模糊的。例如,某些业务服务可能
被实现为基本数据库管理系统中的触发器或存储过程。
术语“服务”是指“递交的功能”。术语“部件”是指“在实现时被封装在一起的一个
或多个服务”。

四.根据需求确定服务在使用服务模型进行开发时,需要逐步确定应用程序需要的服
务。

服务模型是一种应用程序设计方法,可以用于指导整个的开发过程:从需求说明到基于
部件的应用程序部署。最开始,需求被映射到服务,然后将服务映射到部件,部件是真
正的软件,它们可以分布在不同的服务器和工作站上,组成最终的应用程序。
在从应用需求中概括出服务的过程中,首先需要确定各种概念性的“业务对象”。“业
务对象”是指提供或者使用服务的功能实体。另外,还需要为每一种服务设计出系统接
口。
业务对象
对于几乎所有的应用需求,将应用程序设计划分成若干子系统都是必要的。在子系统
中,又可以用类似的方式再次进行划分,直到划分出的小块足够的小,可以作为问题的
某一方面作出明确的定义。这些方面常常被称作业务对象,也就是业务感兴趣的对象或
实体,例如客户或订单。问题到底被分成多少“级”将取决于原始问题的大小。
利用业务对象,可以在逻辑上封装服务,为业务的运作提供功能组合。为了处理服务种
类繁多的复杂情况,必要时可将特定业务对象所需的全部服务包装在一起。
在利用服务模型设计应用程序的时候,需要确定要“请求”每个业务对象作什么,而它
能“提供”什么。在这种意义下,服务是一组相关的功能,用来处理常见的业务活动或
提取相关的信息。
业务对象是一种逻辑单元,但不一定是物理单元。在实际实现中,一个物理部件可能包
含几个业务对象,也可能包含一个或几个业务对象的一部分。
系统接口
为了有效地使用业务对象和服务的概念,需要进行结构化的功能划分,并定义功能块之
间的相互操作。在将它们实现为部件时,需要认真定义每个部件与系统其它部分之间的
接口。在开始实现之前,甚至在设计的最早阶段,就应该考虑到这些接口的基本要素。
要先在逻辑层上进行定义,该接口并不需要考虑到具体的物理细节,但是它必须描述系
统内的数据流、事件以及信息。部件本身可以被看成是一个黑箱,只需要确定它的输入
条件和输出结果,而不需要关心内部的细节。

(一)确定所需的服务
在确定服务并把它们组织成业务对象时,需要遵循一定的步骤,下面归纳出其中的要
点。业务对象最终将被实现为部件或者一组部件:
1. 确定业务解决方案必须提供的功能。创建使用脚本是一个良好的开端,本章的“确
定所需的服务:示例”一节将讨论这个问题。
2. 根据功能需求确定业务对象和服务。随着后续步骤中细节的增加,这个过程将反复
进行下去。
3. 把所有服务分为三种基本类型的服务(用户、业务或数据)。
4. 对每一个服务,确定所需的数据和功能,以及它与其它的服务提供者之间的关系与
相互依赖关系。
5. 决定服务的组织层次。定义必要的接口,以便与其它服务进行通讯。

一个业务对象可以包含实现其功能的三种基本服务类型,也可以仅包含一种类型的服
务。例如:一个“客户”对象可能包含所有的三种服务类型(他的购物和订货历史记
录、根据过去的购买情况确定折扣、输入和浏览订单)。另一方面,“库存清单”对象
可能只包含数据服务。
在评价整体和子系统的多种实现方案时,将业务解决方案描述为“服务网络”是很有帮
助的。对服务的描述(只与业务有关)不应该受到特定技术的局限,尽管技术确实会影
响对它们进行描述的方式。但是,从部件的角度来看,如何实现业务是相对独立于技术
的。
在这一阶段必须理解的一点是:业务问题能够被分解为若干服务的集合,从这些服务推
导出部件,部件之间相互作用形成应用程序。
(二)确定所需的服务:示例
为了说明如何确定所需的服务,可举一个例子。开始,先分析构成业务问题的最高层业
务对象。下面讨论的是一个简单的订货处理问题。
有很多方法和表示法可用于建立以下示例的模型,但是这里最关心的并不是采用何种表
示法。为了阐述清楚,使用简化的表示法就可以表达最主要的观点;在实际工作中,应
该使用最方便的表示法。
简单地描述需要解决的问题,然后说明如何解决这个问题。在该例中,需要解决的业务
问题是接受并处理客户的订单。开始需要向该系统的用户了解情况,然后写出一份叙述
性的用户使用脚本。在进行了几次交流之后,可能形成如下的解决方案说明:
“客户向我们订购产品。我们把产品发给客户,并且更新库库存清单。在货物发走之
后,开出一张发票并邮寄给客户”。
在分析这种叙述性的使用脚本时,经常会发现名词对应于业务对象,而动词可以对应于
服务。在这个例子(高度简化的)中,名词包括:
■ 客户
■ 产品
■ 库存
■ 发票

与此相关的动词有:
■ 定货
■ 发货
■ 更新
■ 开发票
■ 邮寄

下一步,需要精确地确定每一个业务对象应有的特性和功能(作为功能说明的一部
分)。从动词列表出发(利用背景知识),得到每个业务对象的主要功能,如下所示:
这些功能(以及其它的功能)还可以进一步进行细分,但是通常情况下,可以认为这些
功能就是满足业务问题(处理订单)所需的服务。
这是一个不错的开端。已经确定了一组服务,并且能够勾勒出初步的子系统。现在,可
以关注最主要的业务服务了。在功能说明中,还需要说明其它的需求:在提供用户服务
的时候如何进行显示和输入。根据业务服务,可以确定需要提供的数据服务。下面的逻
辑服务模型(为了清楚起见,压缩了数据服务层)给出了完整的“服务网络”和子系
统。
注意,该例子最后被划分为五个较大的子系统,每一个子系统包含了许多服务,服务之
间存在着相互作用。这种分组是功能上的,并不意味着需要五个单独的物理部件。

(三)服务间的相互关系
为了满足某些动作或信息请求,一个服务有时需要调用其它的服务。服务间的相互作用
可以借助于消费者与供应者的概念进行说明。
消费者与供应者
服务对于其使用者来说是供应者,服务的使用者是消费者。良好的服务应该是其使用者
的供应者,又是其它服务的消费者。
每个服务都应有良好的接口,使其它消费者能够使用它提供的功能。
服务的重用不仅限于与它有供应者-消费者关系的服务。在完成开发之后,封装了服务
的部件被编入.Visual Basic Component Manager 的目录中,以备其他开发者查看和重
用。
关于部件管理器的其它问题,请参阅“客户/服务器工具”。
调用服务
服务的行为细节被封装起来,只能通过定义良好的公开接口调用服务。服务仅需要知道
下列协议:如何向其它服务发送请求,如何处理应答或结果。
为了编入目录以供重用,所有的服务都应说明它们的输入参数和返回值。
某些外部事件或系统状态的变化可能引起服务间的相互作用,这可能需要调度(可能是
通过时钟),也可能是没有调度的(可能是被用户的动作或其它的服务请求触发)。
在定义服务时与实现有关的问题
在实现时,应用程序服务被装入软件部件包中。服务的实现应该遵循以下原则:使用子
模块、调用方式简单、维护成本低。通常需要对颗粒度加以权衡:颗粒度越低,子模块
就越多;但是子模块越多,系统就越复杂。
还需要权衡性能、安全性、网络带宽以及其它因素。例如,某些业务规则本来可以实现
为 SQL 存储过程,为了使编码和调试的效率更高,可以将其实现为 Visual Basic 的
远程自动化服务器。换一个角度来看,为了安全性或是数据传输方面的原因,最好是在
DBMS 中实现业务规则。
每类服务的实现通常都采用特定的工具和技术,从而使每类服务的开发组只需集中精力
研究专门的技术领域。例如,业务服务组可能只需要擅长使用 Microsoft C++ 和
Remote Automation 对象的创建,而不需要对 Microsoft Visual Basic 或某种 DBMS
有深入的了解。
服务类型之间的接口可以使用标准的工业技术,例如 Windows Open Services
Architecture (WOSA) 和 Component Object Model (COM)。如果一个公司遵循这些规
则,将能够从市场上购买其它厂商(甚至是在同一业务领域的竞争者)提供的合乎这些
标准的部件。
关于实现问题的详细讨论请参阅“设计指南”。

第三节 客户/服务器工具

描述了企业版中用于在网络上开发、测试和使用基于部件的应用程序的工具。这些工具
同时支持远程自动化和分布式部件对象模型 (COM)。

Microsoft Visual Basic 5.0 企业版,提供了在三层服务模式基础上建立基于部件方
案的设计策略。本章介绍用于建立这些分布式解决方案的软件工具和实用程序。
此处描述的工具和实用程序,允许企业开发人员把业务规则或数据服务封装到可重定位
的部件中,在物理上,这些部件与用户界面和应用程序逻辑上是分开的,允许在网络上
的任何地方部署和共享它们。
这些工具所包括的功能和实用程序,既支持远程部件交互的分布式 COM,又支持这种交
互的远程自动化。这些工具包括一组创新的设计和调试功能,以创建与远程客户/服务
器数据库交互的对象,这些工具中还有功能齐全的性能资源管理器,有了这个管理器就
可用不同的部件配置方案进行试验,并在自己的网络上从物理上度量它们的相对执行能
力和弱点。
下列内容简要介绍和总结了每个工具的用途和优点,从而可以纵览企业版全貌,了解它
的功能。
主题
一. 分布式 COM 和远程自动化工具介绍工具和概念,以协助使用分布式 COM 和远程
自动化,使得部署和访问远程 Windows NT 和 Windows 95 系统上的 ActiveX 部件
(以前是 OLE Automation 服务器),就象处理本地部件一样。

建立基于部件的解决方案,需要预先建立部件,并为通过网络进行远程访问而部署部
件。它的复杂性是三层结构的客户/服务器方案没有被广泛采用的主要障碍。
Visual Basic 4.0 版在远程自动化的介绍中讲述了这一不足。目前已采用分布式 COM
改善其性能,分布式 COM 增强了行业标准化部件对象模型。远程自动化仍然保持向后
兼容性,可以部署在 16 位系统上。4.0 版提供的所有远程自动化实用程序都支持两种
远程系统。
Visual Basic 允许创建 ActiveX(以前是 OLE Automation)部件,也允许在远程
Windows NT 和 Windows 95 系统上部署和访问这些部件,同处理本地部件一样。客户
执行与服务器执行没有任何区别,所以可在客户工作站或远程服务器上运行 ActiveX
部件,而不用重新编译客户应用程序或 ActiveX 部件。
(一)DCOM与远程自动化之间的区别
在单机上,对于 ActiveX 部件与客户进程的连接的每一端,部件对象模型都自动提供
代理/通讯模块组合。
就物理上独立的机器而言,分布式部件对象模型对机器上的部件本质上是一视同仁的,
都提供透明的代理/通讯模块和网络传输服务。
远程自动化提供额外的软件部件,即自动化管理器,并用那些能够通过网络进行通信的
模块取代原代理/通讯模块组合。
自动化管理器驻留在远程计算机上,并把整理好的(已打包的)请求从本地机器上的远
程自动化代理传送给服务器上相应的远程自动化通讯模块。自动化管理器将排列返回
值,并通过远程自动化代理返回到 ActiveX 客户部件。不论客户机还是服务器,都不
知道连接是远程的。
下面各部分将进一步说明注册、编目和导出 ActiveX 部件的实用程序。这些实用程序
包括:
■ 客户注册实用程序
■ 远程自动化连接管理器
■ 缓冲池管理器
■ 部件管理器

建立和测试远程部件
为了建立和演示分布式 COM 以及远程自动化,在 Visual Basic 安装的
\Samples\Entrpris\Hello 目录中提供了一个简单的示例应用程序。在《程序员指南》
的“发布应用程序”的“安装远程自动化和分布式 COM 部件”中,提供了在不同的机
器上分别建立客户和服务器部件的指令。
按照指令安装并注册部件后,就可以在本地计算机上运行客户端应用程序
(Helo_cli,exe),而服务器端部件 (Helo-svr.exe) 既可以本地运行,也可以远程运
行。要实现服务器端部件的部署在本地和远程切换,应使用远程自动化连接管理器,这
将在本章后面介绍。
(二)客户注册实用程序
 客户注册实用程序用于在客户机上为远程执行注册 ActiveX 部件。该实用程序要用到
Visual Basic 建立的部件的 .exe 文件和同时生成的 .vbr 文件。
客户注册实用程序有两种版本:Clireg16.exe 和 Clireg32.exe。16 位版用来为 16
位客户应用程序注册 ActiveX 部件。32 位版用来为 32 位客户应用程序注册部件。如
果相同客户机器上的 16 位和 32 位应用程序都用到服务器,则必须同时使用这两个注
册实用程序。
客户注册实用程序是命令行程序,主要由安装程序用来“暗中”注册 ActiveX 部件。
对于交互式连接管理,后面讲述的远程自动化连接管理器更便于使用。
(三)连接服务器
远程自动化连接管理器实用程序 Racmgr32.exe 位于 Visual Basic 的 \Clisvr 子目
录中。它使 ActiveX 部件注册从本地到远程的切换更加简单,同时简化了设置访问安
全性选项。不要被其名称所迷惑,实际上,对于分布式 COM 和远程自动化所使用的部
件的注册,它都能胜任。
使用远程自动化连接管理器
连接管理器左端的列表框可显示类,而且 Windows 注册表把这些类当作潜在的
ActiveX 部件列出。在右端的标记对话框提供访问和连接设置值。
 “服务器连接”选项卡提供了一些选项,从而可在本地机器上快速方便地测试、调试
远程自动化部件,然后直接为远程使用来部署它们,即使客户部件正在运行也可以这
样。
对于远程访问,对话框中的三个字段分别指定服务器名字、协议和身份验证选项。下例
所述的是这些字段的可能选项。
字段     示例项

Network Address  VBSQLSVR
Network Protocol  TCP/IP
Authentication Level  (No Authentication)

要在远程访问和本地访问间进行转换,应在服务器名字上单击鼠标右键,并在已出现的
上下文菜单中选择“远程”或“本地”。从列表中选定多个项就可同时改变几个类的注
册。
“客户访问”选项卡允许在四个服务器安全性选项中可视地选择。
■ “禁止所有远程创建”禁止客户远程访问所有类。
■ “允许通过关键字进行远程创建”只有在选定了“允许远程激活”复选框时才允许
从类创建对象。这就在 Windows 注册表中改变了它的 CLSID,从而包含以下子键设置
值。
 AllowRemoteActivation=Y

 选择该选项就启动了“允许远程自动化”复选框。
■ “允许通过 ACL 进行远程创建”只有当 Windows 注册表中的 CLSID 的“访问控件
列表”包含该用户时才允许从类创建对象。这只对 Windows NT 有效。
 选择该选项后就可使用 Edit ACL 按钮,通过该按钮可编辑访问控制表。
■ “允许所有远程创建”允许创建任何对象。

(四)缓冲池管理器
ActiveX 部件被调用时就已创建好还是接到请求时才创建,是影响远程部件部署性能的
重要因素。大型 ActiveX 部件服务器要用几秒钟进行初始化和加载。为了避免这种性
能冲突,可用缓冲池管理器概念来维护预创建对象缓冲池,一接到请求就把缓冲池分配
给客户。
\Samples\Entrpris\Poolmngr 子目录中的 PoolMngr 示例工程是一个工作示例,说明
如何实现缓冲池管理器。该示例应用程序是简单的缓冲池管理器,可为客户应用程序维
护已打开的 ActiveX 部件实例。在这一方案中,客户部件向缓冲池管理器请求引用要
使用的对象。缓冲池管理器检查资源缓冲池,然后接受或拒绝该请求。该方法有以下几
个优点:
■ 避免为每个客户请求耗费冗长的对象创建,因为缓冲池通常在客户需要对象之前就
创建好了。
■ 根据客户请求的频率、潜在的客户数量和服务器任务持续时间,可创建(甚至根据
要求去调整)缓冲池的大小,这比服务器到客户的一对一分配要小得多。事实上,合理
的冗余是允许的— 例如,五个 ActiveX 对象实例的缓冲池大小可满足六十个客户的正
常需要。
■ 它用一个阈值限制可创建的特定部件实例数量,该阈值已由服务器管理员确定。这
是非常有用的调整参数。

用来初始化、调整和管理缓冲池大小的调度算法可以简单,也可以复杂,这要根据需要
而定。管理缓冲池大小的自定义因素还要包括最高加载时间和任务的紧迫程度。
因为 PoolMngr 示例工程是用 Visual Basic 编写的,故容易改动,以确定自定义方
案,该方案根据具体环境要求而制定。
另一个有价值的远程部署管理方案是队列管理器,该管理器在应用程序性能评测器中实
现,本章后面部分将会讨论到它。

 二.UserConnection 设计器介绍 UserConnection 设计器,有了它,在设计时就可创
建连接、查询(基于 RDO rdoConnection 和 doQuery 对象的)对象、并一直把它们当
作工程级对象。

UserConnection 设计器使用 Visual Basic 新的 ActiveX 设计器体系结构,对要编程
的数据访问提供设计时的支持。它允许在设计时创建连接并查询对象(基于 RDO
rdoConnection 和 rdoQuery 对象)。并把这些连接和查询对象当作工程级对象。可预
先设置属性、定义新属性和方法并给对象编写代码来捕捉事件。
这不仅为响应由连接和查询而引起的事件,而且为在运行时调用已存储过程和客户定义
的查询提供了简单的方法。
下面是使用 UserConnection 设计器的基本步骤:
1. 在“部件”对话框的“设计器”选项卡上,选中 Microsoft UserConnection,于是
就启动了设计器,而对话框可在“工程”菜单上获得。
2. 在“工程”菜单上,从“添加 ActiveX 设计器”项中选定 Microsoft
UserConnection,把 UserConnection 设计器插入到工程中。
3. 用 UserConnection 对象的属性页建立连接信息,为可在设计时设置的属性指定设
置值。
4. 在目标数据库中选择存储过程,或为用户定义的查询插入 SQL 代码,从而为连接创
建新的 Query 对象。
5. 在属性页上设置属性,配置每个 Query 对象。
6. 编写 Visual Basic 代码,创建新 UserConnection 类的实例,然后象调用方法一
样调用它的任一查询。

(一)插入新的UserConnection对象
 插入新的连接对象的步骤
1 确保在“引用”对话框中引用了 Remote Data Object 2.0 库,该对话框可从“工
程”菜单获得。
2 确保在“部件”对话框的“设计器”选项卡上选中“Microsoft UserConnection”,
从而启动设计器,该对话框可在“工程”菜单上获得。
3 在“工程”菜单上,从“添加 ActiveX 设计器”项中选定“Microsoft
UserConnection”,从而把 UserConnection 设计器插入到工程中。
4 在“插入基类”对话框中键入 ConnectionDesigner.MyConnection,其中,
MyConnection 是要给 Connection 类起的名字。

连接设计器属性窗口
当插入 UserConnection 设计器时,新的连接类已添加到了当前工程中。并出现该类的
可视化表示,被其属性窗口所覆盖。
UserConnection 属性窗口有以下三个选项卡:Connection、Authentication 和
Miscellaneous。
Connection 属性
Connection 属性用于在设计时(为检索可用的已存储过程的列表)建立与服务器的连
接,并保持在新的 UserConnection 对象中。因而,该类在运行时被创建不必指定连接
信息。该连接信息由数据源名字(或者,如果选定了 Use DSN-less Connection
String,则为驱动器和服务器名字)与“其他 ODBC 属性”框中指定的任何专用连接令
牌一起组成。
“使用 ODBC 数据源”框显示当前计算机上的所有 DSNs。也可通过单击“新建”按钮
来定义新的 DSN。
Authentication 属性
“身份验证”选项卡包括连接到数据源所需要的信息,而数据源是在连接信息选项卡中
指定的。这些连接的属性可能和新的连接对象一起被保持,也可能不会,这要根据页底
的复选框的设置值来决定。详细信息请参阅下面的“身份验证的持久性”。
“用户名称”和“密码”文本框都自带说明。在输入密码时,密码框显示星号。
“提示行为”组合框允许选择 rdoConnection 对象的提示性能。
身份验证的持久性
由于事关安全性和密码保护,所以对 UserConnection 对象的身份验证属性有两个级别
的持久性可用。缺省情况下,这两个级别的持久性都被关上,所以不会出现身份验证属
性的隐藏或持久。
如果选择“保存新建 Run-mode 类的连接信息”,则用户名和密码属性被存储在实际的
类属性中,并在编译好的可执行文件或 DLL 中被保持。
如果选择“保存设计时的连接信息”,则用户名字和密码属性只在设计时被保持,未被
写入编译好的 .exe 或 .dll 文件中。
Miscellaneous 属性
连接属性页上的最后一个选项卡允许开发者为新的 UserConnection 类调整 Timeout
属性和光标库。
可将“登录超时”和“查询超时”值设置成 RDO 2.0 中允许的任何值。
“游标驱动程序”组合框包含的选项映射为 "CursorDriver" 属性的 RDO 常数。
UserConnection 设计器窗体
在输入属性建立 UserConnection 对象的 ODBC 连接之后,单击“确定”关闭属性窗
口。新的对象显示在主 UserConnection 窗体中。
窗体顶部的工具栏按钮允许添加单个或多个查询、删除查询、检查或改变查询属性以及
改变 UserConnection 设计器的全局选项。
(二)插入新的Query对象
新的 UserConnection 对象插入到工程中后,就可为它添加新的 Query 对象。这些查
询对象可以是存储过程调用,也可以是用户定义的 SQL 语句。设计时定义的每个查询
对象都自动被当作新 UserConnection 类的方法来使用。
为了添加 Query 对象,应单击工具栏上最左面的按钮。新的 Query 对象出现在
UserConnection 对象层之下,并显示其“属性”窗口。
选定存储过程
选定“插入存储过程”来查看数据库上可用的存储过程。UserConnection 设计器试图
使用用户输入的连接属性来建立与数据库的连接。如果成功,它将列举数据库上可用的
存储过程,并允许选定一个过程来定义 Query 对象:
插入用户定义的查询
选择基于用户定义的 SQL,来创建针对远程数据库的本地查询。这将启动 SQL 属性
框,可在该属性框中输入 SQL 语句定义查询。
也可以单击“创建”按钮,打开 MS-Query 查询设计工具。MS-Query 提供可视化界
面,允许拖放数据库表和字段,定义条件并设置关系来定义查询。在退出 MS-Query 时
自动把可视设计的查询转换为 SQL 代码,并将其插入到用户定义的 SQL 属性框中。
详细信息   关于使用 MS-Query 查询生成器的详细信息,请参阅 Visual Basic CD 的
\Tools 目录中的 MSQry.hlp。
设置其它查询属性
除上述定义的属性外,Query Property Page 还有另外两个选项卡用于定义
Parameters 和高级属性。
“参数”选项卡在查询属性页上,允许开发者为当前查询中的每个参数调整属性。
可自动从查询源来决定“参数”列表框中显示的参数,但不能在此改变这些参数。在查
询的 SQL 语句(或已存储过程的调用语句)中对每个参数标记都生成参数。根据 ODBC
规范,参数标记由一个“?”来指定。例如,下面的查询包括两个参数:
SELECT * FROM authors WHERE state = ? AND zip = ?

可在此改变的参数属性是名称、方向、ODBC 绑定数据类型和 Visual Basic 数据类
型。
如果参数的实际名字很复杂或不直观的话,可以改变 Name 属性,使 Visual Basic 代
码通过熟悉的名字来识别参数。
对于某些 ODBC 数据库,驱动器不能确定参数的方向(输入、输入/输出、或返回
值),所以,对这些数据库有必要设置参数的方向。同样,为使数据类型成为代码能够
使用的 Visual Basic 数据类型,需要覆盖缺省类型转换。
高级的 Query 属性
“查询”属性页的“高级”选项卡通过设置限制和阈值来微调查询。
 “调用语法”编辑框允许改变调用语法,而且 RDO 将用这种语法来调用存储过程。编
辑它可以调整参数数量和返回值的表示。编辑调用语法是高级操作,应该只由了解细节
的开发者来做。
在代码中使用 UserConnection 对象
就象对待任何类一样,可把 UserConnection 对象插入到 Visual Basic 代码中,创建
对象变量,并用已定义的 UserConnection 类的新实例来说明。例如:
Option Explicit
Private MyConnect As MyConnection
Set MyConnect = New MyConnection

可在插入的 UserConnection 对象“之后”编写自己的代码,清除由连接或它所定义的
任何查询引起的事件。也可实现自己的方法,或编写 Property Get 和 Property Let
过程来实现自己的属性,并将这些属性添加到新对象的类型库中。

三.T-SQL 调试器介绍 T-SQL 调试器,该工具可交互式地调试已存储的远程过程,该
过程是用 Visual Basic 5.0 版开发环境的 Microsoft SQL Server's Transact SQL
语言编写的。

T-SQL 调试器完全与本章前面所介绍的 UserConnection 设计器集成在一起。它允许交
互式地调试用 Microsoft SQL Server 的 Transact SQL 语言编写的已存储的远程过
程,而该语言是 Visual Basic 5.0 版的开发环境内的语言。用 T-SQL 调试器可以:
■ 显示 SQL 调用堆栈、本地变量和 SQL 已存储过程的参数。
■ 控制和管理断点。
■ 查看和修改本地变量和参数。
■ 查看全局变量。

安装和兼容性
为了使用 T-SQL 调试器,必须拥有 SQL Server version 6.5 以及作为数据库服务器
安装的 Service Pack 1。调试器使用 SQL Server 的 Sdi.dll 所显露的功能,并通过
远程自动化来显露该功能。
在安装 Visual Basic version 5.0 时,选择安装所有企业版工具,则能够正确地完成
安装和配置 T-SQL 调试器的客户端部件。如果需要重复安装过程,在“CD 安装”对话
框中选择“自定义”,对于“企业版工具”选项选择“全选”。
服务器端的安装
随着 SQL Server version 6.5 和 Service Pack 1 的安装,可在服务器上安装和注册
SQL 调试器接口和远程自动化部件。这些部件位于 Visual Basic 安装的
\Entrpris\Tsql\SrvSetup 目录中。在 Windows NT 4.0 上,只需直接运行安装程序
Sdi_nt4.exe 即可。
注意   为了在 NT Server 3.51 上进行安装,必须手工复制和注册必要文件。此过程
的全部指令都在 \Entrpris\Tsql\SrvSetup 目录的 Readme.txt 文件中。

使用 TSQL 调试器
调用 T-SQL 调试有三种不同的方法。
1. 在设计时调试存储过程或批查询,可通过 Visual Basic 的外接程序管理器添加
T-SQL Debugger Add-In。之后,在外接程序菜单项下选中 T-SQL Debugger 启动它。
然后只需选一个 DSN,选择存储过程或批 SQL,按“执行”按钮。就可以调用调试器并
调试 SQL 了。
2. 在 Visual Basic UserConnection 对象内部调试存储过程,可在 Query 对象上单
击鼠标右键,从菜单中选择“调试存储过程”,启动该 Query 的存储过程的调试对
话。
3. 在调试 Visual Basic 代码(运行时调试)的同时调试存储过程,可在 Visual
Basic 的“工具”菜单的“选项”对话框中选中“T-SQL 调试器选项”,则可以:
■ 打开自动跟踪存储过程,每次逐语句运行到 RDO 方法(要执行存储过程)都会启动
T-SQL 调试器。
■ 打开安全模式,自动回滚设计时调试的查询。
■ 调试设计时查询时,限制出现在 T-SQL 调试器的输出窗口的行数。
■ 设置调试器在连接到数据库时使用的登录超时值,以得到内部 SQL 状态。
 选中“通过 RDO 连接自动地逐语句运行存储过程”复选框后,如果逐语句运行 (F8)
到一行代码,该行代码执行调用存储过程的 RDO 方法,则自动启动调试器。这样就可
以逐语句运行存储过程并继续调试 visual basic 代码。

注意   如果存储过程在完成之前产生了足够的数据填充缓冲区,那么 SQL Server 将
在存储过程执行结束前返回。此时,T-SQL 调试器和 Visual Basic 调试器将同时激
活。Visual Basic 代码必须在存储过程完成其执行前从 RDO 取出结果集。还要确保
basic 代码通过设置 Visual Basic 为运行方式 (F5) 读取结果集,并且在需要中止执
行的位置设置断点。使用任务栏或 Alt-Tab 键可在 Visual Basic 和 T-SQL 调试器之
间切换。
 启动调试器后,将建立 ODBC 连接并显示“输出未赋值的参数”对话框。
在 Value 字段中输入任何未赋值参数的值,然后单击“确定”。随后就出现了 T-SQL
调试器界面,并显示已存储过程的文本.
调试选项
随着所显示的 SQL 语句,在工具栏按钮和“调试”菜单上的几个调试选项是可用的。
这些选项包括:
■ 运行
■ 设置和清除断点
■ 单步执行
■ 逐语句运行子表达式
■ 逐过程运行子表达式
■ 运行到光标处
■ 停止调试
■ 重新启动

视图和选项
除“代码”窗口包含正在调试的 SQL 语句外,TSQL 调试器界面还为局部变量、全局变
量以及查询输出(结果集合)提供不同的输出窗口。“查看”菜单还允许打开独立的
“字体”窗口和“临时表转储”窗口,因而可在代码执行时检查这些内容。
通过“选项”菜单可改变显示的字体和颜色,以定义 T-SQL 调试器的外观。
从 T-SQL 调试器中退出
当完成了调试会话时,可选择“文件”菜单的“退出”命令来关闭调试器并返回到
Visual Basic 工程中的 UserConnection 查询。
为再次执行该查询,可选定“调试”菜单的“重新启动”命令。
疑难解答
如果使用 T-SQL 调试时有问题,需要检查服务器上的事件日志。SDI.DLL 将在事件查
看器的应用程序段记录事件。COM 或 分布式 COM 错误将在查看器的系统段记录事件。
■ 确保两台机器能够通讯。如果是使用 TCP/IP,只需在服务器的命令状态下键入
ping 以及客户机的名称。如果失败,检查并修复两台机器之间的连接问题。
■ 确保 SDI.DLL 和 SQLSERVR.EXE 在同一目录下。即,在 SQL Server 目录下的
binn 子目录。缺省为 c:\mssql\binn。
■ 确保服务器端的 RPC 服务已经启动。可以启动控制面板,打开服务应用程序,检查
远程程序调用 ( RPC ) 服务和远程程序调用 ( RPC ) 定位程序正在运行并设置为自动
启动。
■ 确保 SQL Server 没有被设置为以 SystemAccount 登录。可以启动控制面板,打开
服务应用程序并双击 MSSQLServer 服务。如果服务被设置为以 SystemAccount 运行,
更改它使服务器以所在域一个合法的帐号登录。如果调试仍然失败,那么应确保 SQL
Server 的启动帐号有足够的权限在客户机上运行自动化服务器。
■ 如果在事件日志看到 COM 错误 80080005。应确保未从命令状态启动远程自动化
 autmgr32 )。Autmgr32.exe 只能运行在以 SQL Server 的登录帐号登录的 windows
工作站。其它的 windows 工作站会产生问题。如果是这样,通过任务管理器关闭
autmgr32.exe 并让 sdi.dll 和 autprx32.dll 通过 COM 加载 autmgr32。
■ 如果在服务器和客户机上都未安装和加载分布式 COM ( DCOM ),应确保远程自动化
成功地安装在服务器和客户机上。
■ 如果客户机是 NT 4.0 或更高的版本,应运行 DCOMCNFG 并确保每个用户都具有对
vbsdicli.exe 运行和访问的权限。

 四.应用程序性能评测器介绍一个(用 Visual Basic 编写的)软件实用程序,该程
序可直接运行自动化的“what-if”测试,以配置不同网络拓扑结构中的多层应用程序
的性能,并考虑诸如网络带宽、请求频率、数据传递要求、服务器性能等因素。此外,
APE 本身就是设计得很好的分布式应用程序的例子。

“应用程序性能评测器”(APE) 是用 Microsoft Visual Basic 编写的软件实用程序,
用来协助分布式客户/服务器应用程序的设计、计划部署和性能调整。有了 APE 就很容
易运行自动的“what-if”测试,以便在不同的网络拓扑结构中配置多层应用程序的性
能文件,并考虑网络带宽、请求频率、数据传输要求、服务器能力等因素。
此外,APE 本身也是一个设计得很好的分布式应用程序的示例。其 Visual Basic 源代
码有很好的注释,结构也合理,可作为基于部件的客户/服务器应用程序的“模板”。
很容易用源代码作为自己的多层方案设计的起点。
详细信息   可在 Visual Basic 安装的 \Samples\Entrpris\Ape\Src 子目录中找到应
用程序性能评测器的源代码。

在分布式环境中配置性能文件
多层客户/服务器应用程序把功能分割成独立的部件,这些部件可通过种类广泛的物理
部署配置分布在网络上。对这些分布式应用程序的设计器来说,关键性能问题不是调整
不成熟的代码执行,而是为分布式的方案确定最适宜的网络拓扑结构。
“应用程序性能评测器”允许在不同的网络配置中快速建立测试情况并测量不同条件下
的性能,从而协助这种设计。用 APE 来探究可选的方案很容易说清楚以下问题:
■ 一定更有效地将服务打包成一个大型部件,还是用许多较小的部件将功能分解?
■ 更好地在远程服务器上部署特殊的计算加强型任务以利用最佳计算资源呢,还是在
本地桌面机器上把网络通信量最小化?
■ 当添加更多的客户时会对方案有什么影响?
■ 在最坏的网络条件期间,方案的瓶颈是什么?
■ 当对后端服务器的请求超过其能力时在性能方面会出现什么问题?

远程部署模型
APE 为远程异步和同步执行与管理对象提供了内置模型。
APE 通过两个独立部件的合作模拟远程任务:运作器(worker)和服务(service)。
这是一种基于部件的良好的设计思想,把执行上下文(运作器)从实际的计算例程或业
务服务(服务)中分离出来。由于远程部件被最有效地包装成进程内服务器,所以这些
部件在执行它的远程机器上需要过程空间。工作者部件提供这个过程空间和执行线程,
而服务则封装应用程序指定的功能。
同步(直接例证)模型
应用程序性能评测器实现同步连接,就象从客户应用程序直接向位于远程网络服务器请
求一样。因为连接是同步的,所以客户应用程序将等候任务完成,而且在服务器返回以
前都被堵塞着。在“模式”菜单上选定“同步”,就可从 APE 的用户界面上直观地解
释了这种模式。

如简图所示,客户创建了运作器,运作器接着又创建执行工作的服务。当完成了服务
时,运作器和服务都被破坏。附加的客户创建附加的运作器和服务,而绝不与其它客户
共享运作器和服务。
异步(排好队的对象)模式
直接实现的一个缺点是启动运作器和服务对象所花费的开销。当运作器结束一个客户
时,运作器和服务都被破坏。
更好的方法是使运作器保持“活动”,减少它们初始化时的开销。对此有许多方法,其
中包括缓冲池管理器和队列管理器方案。如下所示,对于异步操作,APE 实现队列管理
器。
在最简单的情况下,队列管理器创建一定数量的运作器,所有运作器最初状态是“不
忙”。当收到客户的请求时,队列管理器分配一个运作器,把它标记为“忙”,用运作
器来执行服务,直到完成才把它的状态改回到“不忙”。队列管理器接受所有请求,并
按照先到先服务的原则分配运作器。
因为队列管理器在内部维护其队列,所以不会拒绝客户请求。如果所有预先分配的运作
器都忙,则队列管理器直接等到某个运作器可用时才依次把它分配给下一个正在等待的
客户。在分布式方案中,可用这样的队列使运作器一直忙。这就使服务器运行在最佳性
能级上 ─ 即完全加载。
带有回调的、排好队的对象模式
以上仅指出了队列管理器完成工作所需的最少步骤。对于将状态信息返回给客户,或者
为了管理而登录事务信息,都没有作任何安排。
通常可用两种方法之一来传送返回信息:通过返回值同步传送,或通过间接的通知机制
异步传送。如前所述,同步(直接)方法使客户程序处于堵塞状态,等待完成返回。对
于异步操作则需要独立的通知机制。回调对象就提供了这样的机制,该对象可通过
Visual Basic 中的 ActiveX 部件来实现。当服务器端要报告有关事项时,回调允许客
户继续处理并异步地得到通知。
为在队列模式中使用回调,额外的部件:“加速器”被用来排列返回给客户的信息。
在操作中,客户实现一个内部回调类,它具有预先定义的方法。
1. 客户创建回调对象的实例,并把指向它的指针连同其工作请求一起传递给队列管理
器。
2. 队列管理器依次调用运作器,并传递回调对象指针。
3. 完成任务后,运作器用返回信息调用加速器并再次把指针传递给回调对象。
4. 最后,加速器异步回到客户对象中。

正如所示,队列管理器、运作器和加速器都被送进了记录器中。
建立和测试 APE
以下提供三种独立的安装方式
1. 管理器安装─ 这种安装方式安装了为在一台机器上使用 APE 所需的全部部件。其
中包括管理器用户界面、服务器部件和客户。在安装 Visual Basic version 5.0 期
间,当决定安装所有企业版部件时,自动采用这种安装方式。
2. 服务器安装─ 这种安装方式只在机器上安装服务器部件。如果想要把一台机器当作
服务器使用,而又不想使用管理器或任何客户,则用这种安装方式。
3. 客户安装─ 这种安装方式只在机器上安装客户,在多客户方案中用来创建远程客
户。
4. 运作器安装─ 这种安装方式只在机器上安装运作器,在多客户方案中用来创建远程
运作器。

管理器安装包括使用应用程序性能评测器所需的全部部件,其中包括服务器端部件。在
单机上运行时,尽管在这种配置中不能远程连接服务器端部件,但 APE 的功能是完整
的。为快速使用 APE,可在一台机器上运行客户安装,启动应用程序,选择已提供的任
何配置文件,然后单击“启动”。
要执行远程测试,必须在第二台机器上使用 APE 服务器或远程客户安装,以保证能够
网络连接,并在服务器部件上设置远程自动化权限。
在第二台(或服务器)机器上安装期间应设置权限,从而在 APE 使用的类上允许远程
激活:
■ AELogger.Logger
■ AEPoolMgr.PoolMgr
■ AEQueueMgr.QueueMgr
■ AEServerMgr.ServerMgr
■ AEWorker.Worker

注意,本章前面已作过说明,以后可用远程自动化连接管理器修改这些权限。
 执行远程测试的步骤
1 如果想把“远程自动化”当作传送机制使用,则在服务器上启动“自动化管理器”。
如果正在使用分布式 COM,则需执行该步骤。
2 在客户机器上启动“性能评测器”。
3 从下拉式“配置文件”列表中选择异步配置文件。
4 在“查看”菜单上单击“服务器位置”。选择“远程连接到服务器”复选框,输入服
务器的机器名字。
5 在“查看”菜单上,单击“Connection 属性”。单击“使用远程自动化”或“使用
分布式 COM”按钮,选择传送机制。选择两个机器都支持的协议和身份验证级。

现在,已经准备好配置每个部件对象并运行测试方案,如“使用 APE”中所述。
使用 APE
在自己的环境和机器上,应用程序性能评测器有助于探究不同的分布式应用程序方案。
使用 APE 的基本步骤是:
1. 选择模式。
2. 配置部件对象。
3. 单击“启动”按钮,开始测试。
4. 在屏幕底部的结果面板中查看结果。

在以上模式基础上,性能评测器允许控制各种参数以及在应用程序上确定其全部效果。
DPE 中用“空”例程实现所有分布式模式,使得很容易指定自己的部件对象为
 Service。
选择模式
在“模式”菜单上选择“同步”或“异步”。还可通过在下拉式“配置文件”列表中选
择内置的配置文件来选择预先配置的模式。每个配置文件都包含各种部件类型的设置
值,这些设置值共同形成要模拟的方案的“配置文件”。
通过个别配置每个部件还可创建自己的配置文件,然后在“文件”菜单上选择“保存配
置文件”。
配置 Component 对象
分布部件所支持的每种模式都使用相同的部件对象类型集合,并使用用户设置值来控制
它们的各方面功能。对每种对象,通过单击“APE 管理器”界面中对象的表示来配置这
些对象。这样产生的对话框允许指定要创建对象的数量、数据传递选项、定时要求、回
调选项和其它构造对象和使用对象的形式,允许灵活模仿“现实世界”的情节。
每种部件类型都独立提供显示状态窗体和登录事件信息的选项。使用每个选项卡底部的
两个复选框就可启动这些功能。
评测器的有趣问题
应用程序性能评测器是功能强大灵活的工具,并可用它来回答许多有趣问题。最重要的
是,对于改变主要输入参数的数量可直接测试效果:调用的数量、对象的大小、
Service 是进程内的还是进程外的等等。
APE 可协助探究的其它问题包括:
■ 早期绑定和后期绑定相比,性能优点是什么?
■ 当超过网的容量时,模式将会发生什么事情?
■ 对使用异步通知的性能有什么影响?
■ 对详细事件登录的性能有什么影响?
■ 对环境来说,最适宜的部件大小是多少?通过改变 Service 参数和平等取代自己的
Service 部件,可检查不同部件的大小,帮助确定如何将部件细分。
■ 最好的网络协议是什么?使用在“连接”选项卡上找到的协议参数以及任何安装好
的 RPC 传输协议,这有可能与远程对象通讯。
■ 调用安全性的适当级别是什么?使用在“连接”选项卡上找到的身份验证参数就有
可能把身份验证设置为任何被支持的值。通常,身份验证的较高级别增加了经常性开支
─ 控制该参数有助于确定调用安全性对应用程序的影响。
■ 当工作完成时在队列系统上完成的队列的动力是什么?由于可控制 Service 是否使
用处理器循环,所以可以模拟控制队列是在专门系统上操作,还是在另一个远程服务器
上停止。
■ 当这些事情有一个发生时,客户的响应事件将受到什么影响?

示例应用程序

 Hello (Helo_cli.vbp, Helo_svr.vbp)
Pool Manager (Pmgr_cli.vbp, Pmgr_svr.vbp)
APE (Aeexpdtr.vbp, Aeinstnr.vbp, Aelogger.vbp, Aepool.vbp, Aequeue.vbp,
Aeservic.vbp, Aeworker.vbp, Aewrkpvd.vbp, Aeintrfc.odl)为建立和演示远程自动
化,Visual Basic 安装的 \Samples\Entrpris\Hello 目录中提供了一个简单的示例应
用程序。在 \Samples\Entrpris\Ape\ApeSrc 目录中包含有应用程序性能评测器 (APE)
的完整代码和工程文件。而 Pool Manager 示例则放在 \Samples\Entrpris\PoolMngr
目录中。


第四节  开发策略

根据 Microsoft 自己成功的开发策略,介绍了在开发基于部件的客户/服务器应用程序
时管理项目组和进度的新办法。
当今的客户/服务器计算向我们提出了新的挑战,并需要新的开发和项目管理策略。
传统的自顶向下的组织结构和线性的、循序渐进的开发策略十分适合整体的、完备的、
独立的系统。相比而言,基于部件的客户/服务器系统具有分布式特征,使之更适合于
小型的、相互协作的、自主的组模型以及周期性和反复性特点更强的开发过程。
“服务模型”集中论述了基于部件的应用程序模型本身,而开发策略主题的中心是管理
组和过程的创新途径,这些过程包括创建客户/服务器应用程序的过程。
主题
 一.开发模型这个主题介绍两种基于部件的软件开发策略模型:组模型和过程模型。
以这些模型作为大规模项目组织的基础。

基于服务的应用程序由联系松散的部件组成,这些部件来源广泛,包括由内部和第三方
公司开发的部件。此外,如果这些解决方案具有很强的交互特性,则对制定解决方案的
人员来说,同他们的目标用户发展和保持密切的联系比以往更重要。随着当今技术变化
步伐的加快,这些因素加在一起,给技术合作、对外交流和快速的交互式开发带来了极
为沉重的负担。
面对这些挑战,现在的组织需要运用灵活的新方法来开发应用程序。这一章简要介绍
Microsoft 的两种已被成功运用了的主要策略。一个策略是对如何将同一开发项目上的
工作人员组织起来进行指导,而另一个策略是就开发过程自身的方法提出建议。
特别要介绍的两种模型是:
■ 组模型,基于一个小的“同事组”的组织
■ 过程模型,一个反复的、基于里程碑的开发模型

Microsoft 咨询服务 (MCS) 已经把这些策略正规化,并已经用于大型团体客户,这些
客户具有千变万化的结构和五化八门的开发要求。这里所概述的原理和结构甚至已被成
功地引入到团体文化中,而这种团体文化是在自顶向下的组织结构和传统的大型项目模
型上建立起来的。
因为话题太广泛,本章只对组模型和过程模型作概念性的介绍。有兴趣用这些模型进行
项目开发的组织可直接同 Microsoft 咨询服务联系,以获得有关培训研讨班和其它教
育及服务的信息。

 二.组模型客户/服务器开发的组模型基于这样一种想法,即同事组相互依靠,地位平
等。组成员既独立又合作,提倡一种主人翁思想,生产更好的产品。

客户/服务器开发的组模型,是一个小同事组的概念,各成员在工作上相互依赖、分工
协作。每个组成员在项目中都有固定分工,并专注于某个具体的任务。这种工作方法能
够激励主人翁思想,有利于最终得到更好的产品。每个组的领导负责进行管理、指导和
协调,而组成员则专注于执行具体的任务。
在组模型中可识别六个组角色:
■ 产品管理
■ 程序管理
■ 开发
■ 测试和质量保证 (QA)
■ 用户培训
■ 后勤计划

根据项目的大小,每个角色,或者由单人负责,或者由有主管的一组人负责。另一方
面,也可一身多职。
六个项目组角色之间的关系如图 4.1 所示。
图 4.1   六个项目组角色








角色各不相同的成员都以同等身份加入项目组,每个成员都为产品做出一份不同的、但
又是同等重要的贡献。角色是根据组的任务、职责和所要求的技能来定义的。本章稍后
将讨论每个组角色的具体职责。
根据这些相互依赖的组的概念建立起来的项目组织具有下列优点:
■ 使每人都对系统的成功承担风险。
■ 形成一种鼓励清晰、效率、参与、承诺及团体精神的文化。
■ 明确责任,避免个人或组依赖他人完成工作。
■ 允许开发工作集中于创建一个稳定的、顺畅的系统。

相互依赖的组
这些组主要是由一些经验丰富、技术精湛的领导和能够自我激励的组成员组成的,这种
组模型的特点是规模小、凝聚力强。
把每个功能组限定在三到七人之间能有效地保持注意力。如果问题很复杂,使得任何一
种分工都要求一个更大的组,则组领导可把问题分解成能够进行管理的几块。这种注意
力的层次可以减少风险,并更为有效地管理项目。
这里组的概念是指最低层的协作单位。在大型项目上,它是一组开发人员集中精力于一
个少数功能的研制开发,或是处于一个组产品管理员协调之下的一组产品管理员,如图
4.2 所示,组产品管理员在组中扮演着产品管理的角色。
图 4.2   在一个组产品管理员领导下的产品管理员组






















然而,如果项目很小,则整个项目可能被作为单个组来运做。
组角色
这六个组角色都有各自具体的作用和职责。不论这种角色是在小型项目中由个人独自完
成,还是在大型项目中由一个组来完成,这一点始终是如此。后面几节将说明每个组成
员的分工。
产品管理
产品管理员(或产品管理组)为项目建立和维持业务往来,他们在辨别和设置目标客户
的业务优先级中起了关键作用。这包括保证清楚地表达业务所期望的目标,并使其为项
目组所理解,还要保证功能说明书反映业务优先权。
产品管理员掌握项目的构思声明,这是一个非正式文档,用来沟通项目的期望成果和项
目基于的假设条件。
产品管理员也负责高层的项目交流,如业务计划、项目开销、合同协商等。产品管理员
还就最高里程碑与最终用户及组的其它成员进行交流。
程序管理
程序管理员或程序管理组“掌握”关于应用程序的特征和功能的说明书,以便进行日常
协调工作,这对在组织的标准范围内一致、有效地开发和交付应用程序是必要的。
在组模型图中,程序管理处在中心位置,所以,它起着关键的交流和协调作用,利用其
它组主管的输入,程序管理可以帮助产品管理来清楚地阐明项目构思。使用这个构思,
程序管理可草拟出功能说明书的最初版本,因而程序管理被认为是功能说明书的解释
者。程序管理负责有关分析、说明和架构的全部活动。
程序管理还负责确定项目如何同外部标准协调,保持对外技术合作和交流以及管理主进
度的安排。
开发
开发组负责交付一个与功能说明书完全相符的系统。
这个角色的一个重要方面是积极参与制定功能说明书的过程。当提供有关技术可行性的
想法和讨论设计的可选方案时,开发工作与程序管理工作齐头并进,制定出概念明确的
原型。当为功能说明书规定基线时,对于如何解决最重要的问题,以及如何使它们在开
发进度方面职责明确,开发和程序管理必须取得一致意见。
组模型的核心概念是尽早建立基线,然后通过正式的变更控制来管理对基线所做的变
动。例如,在核心功能上取得一致意见后,程序管理就可以配合开发一起为功能说明书
尽快确立基线。随后,在开发工作继续对实现的可选方案进行验证时,程序管理负责来
评价所建议的变更。
测试和质量保证 (QA)
测试和质量保证组验证系统是否符合功能说明书。遵照无缺陷编码的思想,测试/ QA
组将积极参与开发过程,保证质量是在产品制作中形成的,而不是经测试才得以保证。
该分工独立于开发,但又与开发平行,并同开发一起来进行检查和维持平衡。
用户培训(文档)
用户培训组设计、开发并出版印刷品和联机的系统文档,包括各种指导材料。
正如用户所建议,用户培训组参与设计并构造系统和接口原型。它还参与设计并提交安
装程序和过程。用户培训可推动并参与可用性测试,并改进产品的设计,还可同程序管
理和开发密切合作,以确保产品的种类及设计上的变更反映在文档中。
后勤计划
后勤计划组管理着一个从开发到实用的顺利过渡过程,确保能够顺利发行、安装、转交
到实用和支持组。它同开发主管一起工作,确保采用一种便于安装和管理的方式来包装
系统。
后勤计划也同操作人员协作,考虑诸如安装、故障排除、日常管理(包括局域网和服务
器管理)、灾难恢复计划和版本控制策略之类的问题。
后勤计划成员需要极其熟悉组织的文化和基础设施。他们必须理解目标实现环境的性能
问题、容量计划设想和操作程序。
划分组角色
在项目中,项目组的分工可能有一些不同。本章所描述的六种分工指明了一个项目组必
须具有的功能。然而,这并不意味着项目组中必须至少有六个成员。
根据项目和组的大小,可几个人共有一个组角色,也可将数个角色合为一体。图 4.3
说明对三人组承担的小型项目,如何将组角色合并起来。
图 4.3   合并项目分工的示例












对于在一个大型项目上六个成员以上的组,这些组的组织方式乃是将功能组和特性组结
合起来。
功能组
功能组由一个组管理员领导,并由执行相同任务和分工的组成员构成。例如,产品管理
组和后勤组的成员可以组织为功能组,因为他们的职责都同应用程序的整体有关,而不
是与应用程序的特定方面相关(参见图 4.4)。
图 4.4   功能组成员都完成一个单独的任务






特性组
另一方面,一个特性组通常由一个程序管理员领导,并由执行不同任务和分工的组成员
组成。特性组一般由开发人员和测试人员组成,根据产品的一组特征来进行组织。每个
特性组虽小,但和交付完整应用程序的组一样,都有它自己的功能说明书和进度安排,
并严格地执行任务(参见图 4.5)。
图 4.5   特性组是完整的“微型”项目组













总体项目结构
为使“同事组”这一概念可行,要求有一个总体项目管理员,能理解和支持组成员的分
工和能动性。与其说项目管理员直接负责每天的指导和项目跟踪,不如说他是一个推动
者。这个管理员可以是指定的项目管理员、业务或信息系统管理员,这取决于团体结
构。
图 4.6 说明了一个总体项目结构,它包含所建议的项目组分工。









图 4.6   项目报告结构













项目管理组
项目管理组包括项目管理员和若干个主管(每个主管负责一个组分工)。他们共同负责
安排进度和资源,评估风险以及跟踪项目的进度。因为每个组必须完成一组任务,依照
它所做出的承诺来交付成果,所以,关键在于让每个主管都参与项目计划和管理。例如

■ 尽管项目管理员可能要考虑项目的各种特征,初步评估如何应用组模型,但每个组
的主管也都应该有机会来考虑所有的因素,并判断风险。
■ 所有各组的主管都参与功能说明书的协商。对功能说明书的基线所提议的任何变更
都要求得到所有各组主管的同意。
■ 与其从上面将进度计划与看法强加给下属,不如让每个关键人物都有机会对整体的
进度和估计作出自己的贡献。
■ 随着项目的进展,每个组主管都要负责评价风险并参与权衡利弊。

如果组成员的意见不一致,则在必要时,项目管理员自可拍板定夺,统一看法。显然会
有这样的倾向—逐渐向较为传统的自顶向下的项目管理风格过渡。而只有团体文化才能
保证避免这种情况。

三.过程模型过程模型是一种反复的、基于里程碑的开发模型,帮助组织来安排优先顺
序,以并行的策略来推进工作,而不考虑任务的最后期限。

服务模型是高度分布的基于部件的模型,该模型提出一些新看法,将那些开发应用程序
的组组织起来,与此相类似,它也对开发过程本身提出了新方法。
传统的整体应用程序开发通常遵循流水式过程模型,在这种模型中,批准的里程碑是一
个阶段完成和下一个阶段开始的先决条件。这不太适合于客户/服务器应用程序的基于
部件的特征,因为各部件是采用并行方式而不是串行方式开发的。此外,流水式过程模
型还缺乏足够的灵活性和反应速度,这使得它不太适合快速原型开发工具,也不适用于
设计 GUI 驱动的桌面系统用户界面。
由于有了本章中提出的基于里程碑的过程模型,所以能够以并行的和反复的方式开发重
要任务。通过明确组责任,这种模型使得本章所提出的组模型能够最大限度地发挥优
点。它通过评估风险来设置优先权,并鼓励快速地交付应用程序。
(一).传统的过程模型
传统的系统开发生命周期 (SDLC) 是活动的或任务驱动的,曾被许多公司采用。这种传
统的生命周期在各种方法学上有许多变种,通常由以下几个显著阶段构成:
■ 定义   ■ 分析   ■ 设计   ■ 构造
■ 测试   ■ 过渡和移植 ■ 生产
 “阶段”这个术语含有这样的意思,即在下一阶段开始之前必须完成每一组任务。通
常,各个不同的组处理生命周期中的一个阶段,在每个阶段都必须做大量繁重的文档工
作,从而使另一组能够承接起下一阶段的工作。这样做的结果,会过早使决定冻结起
来,从而降低灵活性。
虽然这种模型设法对整个开发生命周期中出现的任务进行分类,但并不识别和影响客户
/服务器开发的各种特性。
使用流水式过程模型来进行客户/服务器开发的主要问题在于它是着眼于任务,而不是
面向过程。这使得它很难作出灵活的决定,以适应迅速变化着的优先权;而对于管理具
有多个部件并且还特别强调桌面用户界面的客户/服务器开发项目而言,这种变化又是
十分重要的。
(二).基于里程碑的过程模型
要克服这些障碍,要有一种更灵活、可反复、面向过程的开发模型。这里,基于里程碑
的过程模型是由产品生命周期模型派生出来的,在 Microsof 公司内,产品生命周期模
型相当成功。基于里程碑的过程模型提倡从过程方面而不从任务方面去考虑工作。过程
中的那些自动调节点都记载在里程碑上。
里程碑驱动的模型具有以下四个特征:
■ 基于里程碑的方法
 应用程序开发过程由外部或内部里程碑驱动,这些里程碑是引导开发过程的检查点。
■ 所有权和责任明确
 过程模型对把每个里程碑的责任与项目组担负的任务挂钩。
■ 风险驱动的进度安排
 尽早完成项目中的高风险部分。
■ 版本发布
 版本发布概念是整个系统开发生命周期中的重要概念,因为对如何提出期望以及如何
计划和管理整个项目,这个概念都会施加影响。
 以后各节中将逐一讨论这些特征。
基于里程碑的方法
如图 4.7 所示,这种过程模型由四个高层里程碑组成。
图 4.7   过程里程碑


   完成代码  QA       构想 批准构思/范围






这些里程碑描绘成螺旋线上的点,而不是直线上的点,以此强调过程是周期性、反复性
的,但不是线性的。里程碑不是冰点。它们是那些可交付的成果被置于变更控制之下时
的“基线”点,这些成果是由里程碑来描述的。这有助于灵活应变以及精益求精。
以下简要说明,这四个主要里程碑:
1. 批准构思/范围里程碑
一旦新应用程序引起人们的兴趣,并得到人们认可,就可组织项目组确定产品。通过构
思提出报告,这个报告划定范围并指点方向。构思/范围批准里程碑对最终用户和开发
组都是一次机会,利用这次机会,他们可在构思和范围方面取得一致意见。
2. 批准功能说明书里程碑
 下一个可视里程碑可用于提供功能说明书基线。说明书详尽介绍了应用程序,因此,
项目组可着手论证资源需求并做出有关承诺。在此里程碑处,用户和组成员要对欲交付
的成果内容达成一致意见,安排优先处理权并提出期望达到的目标。这是一次重新评估
风险的机会,也是对早期进度和资源所做的估计重新进行有效性验证的机会。
3. 完成代码里程碑
  经批准的功能说明书为启动集中的开发工作提供了基线。开发组在过渡阶段设置了许
多中间的交付里程碑,当然,关键的里程碑还是完成代码里程碑。完成代码里程碑为用
户和组成员提供一次机会,对此版本进行最后评估,并验证大批量生产和各种支持计划
以及各项程序是否到位。在此里程碑处,停止所有新开发工作,并把任何未及时完成的
功能记录下来,留待下次发布工作中处理。
4. 产品发布里程碑
 在为编码制定了基线之后,“预发布”的( Beta 版)程序、测试和质量保证活动将
与进一步的编码开发工作同期进行,推动系统朝着发布里程碑进行。发布里程碑就是项
目组正式把应用程序(或“产品”)交付运行并提交给支持组的时刻。

四个里程碑是主要进展时刻,它们体现了开发的所有主要概念。四个里程碑都是复查里
程碑。另外,对各里程碑,可交付的成果并未定型,仍然可作更改。
注意   在整个这一章中,正在开发的应用程序常被看作“产品”。这里所定义的策略
的确是用于开发商业的上市软件,但不仅限于此。更确切地说,形成一种应用程序就是
“产品”的意识将大有裨益,为此,就要迅速开发应用程序,宁愿把将来版本中新的或
有风险的功能搁置起来,严守固定的交接期限和装运日期。

(三).中间里程碑
除非整个交付进度的时间安排只有三个月或更短,否则只有四个里程碑的项目计划并没
有给人以充分的机会来管理和控制项目。所以应建立中间里程碑,每个组主管都要为达
到这些里程碑作出承诺。这使管理组在复查进度和协调交接期限时发现问题,程序管理
也能对整个交付进度表产生的影响作出评价。
对于外部可视的中间里程碑(向整个组织宣布的里程碑)包括:
■ 常规时间表的管理复查会议。出席会议者必须针对可交付的成果评估和复查(正式
的或非正式的)变更请求。
■ 功能说明书草案的可用性。功能说明书应指定临时交付信息模型和用户任务分析。
应当和用户一起复查和验证说明书的有效性。
■ 功能说明书基线之后的会议。出席会议者将批准各阶段成品定型的时间,如构思的
定型、数据库的定型和功能的定型等。

对于开发组内部的中间里程碑,有这样的实例:
■ 短期的、可交付的内部成果。这些可交付的成果应该每隔 5~10 天递交一次,这样
就可以对进展和风险进行现实的评估。
■ 同步化的点。这些点将保证功能说明书的各个成分保持一致,并被同一组假设驱
动。这些点应由程序管理定义,它们包括对数据模型、业务模型、用户界面以及系统架
构进行的讨论。
■ 渐增的内部发布工作。发布工作由开发来确定,最大间隔为六个月(建议三个月交
付一次成果)。

(四).达到里程碑

应当根据每个项目的目标和特征选择通向里程碑的方法和技术。这些方法和技术也应该
与组织的标准和政策一致。
特别要注意这里所讨论的过程模型不依赖于任何特殊的方法论。各组织可继续使用数据
建模标记、用户面谈技巧以及其它方便的分析方法。他们也可选用较新的面向对象的设
计和分析方法。这些方法在自动化工具和培训课程中得到越来越多的支持。
唯一重要的事情就是,技术和方法论应足够灵活,以适应正在发展着的客户/服务器的
实现技术。
所有权和责任明确
过程模型明确每个组成员的责任。如图 4.8 所示,达到关键里程碑的责任要明确地与
每个项目组的角色挂钩。计划、管理和执行与里程碑相关的任务,都是里程碑“拥有
者”的职责。这将鼓励抓重点,促进组合作,这些正是成功交付的关键。
图 4.8   项目计划和管理责任
里程碑 拥有者
批准构思/范围   产品管理
批准功能说明书   程序经理
完成代码   开发、用户培训
产品发布   Q/A测试、后勤计划

按组角色来指定应付责任,这将鼓励自底向上的方法,而不是自顶向下的方法,因此能
够自我调节。在自我调节过程中,如有必要做或想要做的时候,就可以鼓励组成员修改
这个过程。

(五)。定义项目构思范围
产品管理拥有批准的构思/范围里程碑,而程序管理则是主要的贡献者。可交付的成果
就是项目构思/范围文档。当项目组和用户对文档的内容意见一致,而且对文档进行版
本控制时,就算达到了这个里程碑。这时,应在应用程序的概念体系中,借助于使用脚
本、功能以及业务对象,用概念术语来表达用户的需求。随着分析和设计活动的展开,
含义明确的里程碑对项目范围进行复查和评价里程碑将会督促修订和重新评价项目计划
和时间表。
(六)。形成功能说明书
一旦建立起项目构思和范围,所有组成员都要参与制定功能说明书。程序管理保管功能
说明书,并拥有功能批准里程碑的说明书。开发与程序管理一起制定开发计划和时间
表。在交付个人的那一部分功能说明书时,每个组成员都评估风险。组成员可进行并行
复查,开发原型并制定计划,确保制做并交付说明书所要求的东西。
功能说明书可作为所有组成员与用户的一个合同。从总的业务目标、用户类以及每个用
户类希望执行的活动就可产生这个合同。它把用户需求按类分成逻辑用户服务、业务服
务和数据服务。
功能说明书明确定义了应用程序的构思设计、功能接口以及数据需求。它还定义了外部
接口、互操作性、性能目标以及其它把方法结合到方案中去的假设和约束条件。
功能说明书反映了所有组成员达成的共识和作出的承诺,并推进内部进度和对外交流。
(七)。从编码完成到发布
当功能说明书文档已定好基线,开发组基本上就拥有了完成代码里程碑。而且,这又是
一个反复过程,可能包括并行的开发和多种来源的全部部件。它还可能包括许多 Beta
版本或“预发布”版本。
当整个组朝着产品发布里程碑推进时,所有组成员都负有具体责任。在最后阶段,测试
/QA 主要负责核查发布工作是否准备好,并把项目转交给后勤计划。
风险驱动的进度安排
顾名思义,风险驱动的进度安排是尽早对那些代表开发过程中风险最大的因素的任务和
部件做进度安排,并优先进行安排。
以下是过程模型中用来完成风险驱动的进度安排的指导原则:
■ 在拟就功能说明书期间,用来定义开发角色的方法会鼓励人们使用早期概念并实现
原型,以消除或弱化风险。在批准功能说明书之前,开发组要搞清楚是否已了解有关的
风险。随后,这个组将递交一份关于开发和交付的进度安排,并优先安排开发发布工
作。
■ 必须在功能说明书的定义中建立优先安排。基于技术上的风险,该组和用户在优先
权上取得一致意见。这将保证能够最先得到那些有潜在风险的功能和对业务特别重要的
功能。
■ 主要的里程碑不是可交付的成果已定型的时刻,而是在变更控制下放置一组假设条
件的时刻,于是可对这一时刻之后辨识出的风险作出规划并集中处理。
■ 随着功能说明书进一步完善,可能在到达正式的里程碑之前就已启动开发工作。但
放开来说,在此里程碑之前发生的事情都不是开发工作,这没有什么不可思议的。这一
点有助于早期识别风险。
■ 开发工作要使用分阶段的发布进度安排。这通常要涉及 1~3 次中间(beta 或“测
试”)发布。每次发布都会牵涉到发布管理和测试以及 QA,所以,每次发布都好像是
最终发布。
■ 版本发布这一概念是过程模型的一部分。有了这个概念,项目组就能对功能、进度
安排和风险中的利弊权衡做出响应。版本发布还确定一个阶段,在这个阶段要增强第一
次发布基线。

风险驱动的进度表鼓励开发人员朝着早期的里程碑奋力工作,而不是把工作平均分配给
做了风险因素评估的时间段。
另一方面,缺少早期的里程碑是对项目管理的早期警告,应引起对承上启下的“误差”
的注意。项目中在里程碑上早期的“误差”数量可作为一个衡量质量的尺度,并提供预
测信息来调整出现在后期项目中的里程碑。
此外,项目中的风险部分会更加显而易见,从而使用户能以更丰富的方式管理预期目
标。
版本发布
开发组使用分阶段发布进度表。这一般将要涉及一次或多次中间发布。发布管理及测试
和 QA 都将涉及每一次发布,把每次发布都看成是最后一次发布。
基于里程碑的过程模型鼓励项目组把正在开发的应用程序看成一个产品,而把新的发布
和维护作为版本发布来管理。这个概念对如何设置预期目标以及如何计划和管理整个项
目都有影响。
版本发布有助于管理变化不定的局面,明确制定具有激励性的目标,强制结束具有威胁
性的争论,并鼓励持续的改进。
新系统的第一次发布是应用程序的一个基线。尽管还会有新的发布,但第一次发布仍有
助于把应用程序看成一个“产品”。第一次发布中没有包含的特征将被跟踪,并为随后
的发布而得到优先安排。这可通过使用一个数据库来完成,而数据库是用来跟踪想法、
特征和争论点的。该数据库将包含以下内容:
■ 功能说明书中标识了的、被优先安排的特征。这样一来,在根据这次发布的利弊权
衡作出明确决定时,开发可始终集中在优先权高的特征上。另外,从随后的发布考虑,
应捕获当前的发布中没有被纳入的任何特征。
■ 在计划和发展阶段显露出来的好想法。如果这些想法已超出当前发布的构思声明的
范围,也应将它们捕获,以保证在今后的发布中被加以考虑。
■ 在最后的发布中的争论点和非关键性问题。这些都将被捕获,以便在下一个发布中
对它们进行优先安排和访问。

(八)。反复开发
这种反复方法同版本发布密切相关;实际上,版本发布是这种反复方法的一个实例。然
而,这种方法不仅包括发布及待定发布,还包括项目中的所有里程碑和可交付的成果。
从这个意义上来说,过程里程碑可以看成是精益求精的“螺旋中的螺旋”(如图 4.9
所示)。
图 4.9   反复性的方法







在每两个里程碑之间,组成员制作文档草稿,协商内容,并为他们各自的组提供计划方
案。这些组以反复方式并行工作。
在整个系统的开发生命周期中,版本发布这一概念和“产品意识”都是重要的成分,因
为它们将对如何设置那些期望,如何做出决定以及如何计划和管理整个项目带来影响。

(九)。一种多线索的开发方法
基于服务的应用程序模型认为应用程序就是服务的网络。每种服务对其分析、设计和构
造都有不同要求。为了在这种类型的环境中成功地开发软件,需要多线索的开发方法。
分析、设计和构造每种服务的部件要求:
■ 不同的技术
■ 不同的工具
■ 不同的技能集
■ 不同的实现平台
 程序管理负责发挥推动作用,以达到功能说明书的要求,根据这些服务,功能说明书
应抓住被交付系统的需求。应正确观察每一并行的服务类,并由此入手分析系统的需求
和特征。一旦在功能说明书中形成文档,开发组就可做出实现决定,把服务做成软件
包,当作软件部件来对待。尽管在实现中,各类服务之间的一些清晰的区别可能会变得
模糊,但仍可把技能集和工具识别为用户/桌面焦点(用户服务)、企业/业务焦点(业
务服务)和数据焦点(数据服务)。
如本章前面所述,一旦定义各种服务,各个独立部件上的开发工作就可在相互独立的特
性组中并行进行。在中间里程碑上,各部件在预定义的功能层次上集成。这将给各组提
供有价值的反馈信息,并使管理警惕任何可能显露出来的互操作性风险。一直反复下
去,各个部件将继续它们各自独立的,同时又是并行的开发工作,直到达到下一个集成
里程碑。

 四.开发模型概述从大规模开发客户/服务器的角度,扼要叙述组模型和过程模型的主
要特征。

这一节分两个主题:组模型和过程模型来介绍两种重要的开发策略。
组模型
组模型是围绕由工作上相互依赖、协作分工的同等人员组成的小型组的概念建立起来
的。这个模型中定义了六种具体的分工:产品管理、程序管理、开发、测试和质量保证
(QA)、用户培训和后勤计划。
对于特大型的项目,这些组分工可以由组管理员来完成,组管理员又协调若干个小的
“子组”。子组可以是功能组或特性组,并且反过来又可以划分成更小的子组。从最高
层到最低层,每一个组都由不超过 5~7 名的分工明确的组成员构成。
组成员“掌握”着他们正在其上进行工作的那个过程,并且同其它各个组密切合作,采
取一种反复性的、合作的方式来达到一个共同的目标。这将有利于部件的并行开发,并
能支持反复性的、基于里程碑的过程模型。
过程模型
这种过程模型是一种反复性的、基于里程碑的开发过程模型自身的方法。这种里程碑驱
动的模型有四个特征:基于里程碑的方法、清晰的所有权和责任、风险驱动的进度安排
以及版本发布。
在这个过程模型中,里程碑不是那些冻结点,而是基线的可交付的成果被置于变更控制
之下时的那些个点。可以通过一个数据库系统来跟踪变更。通过这个数据库系统,所有
的组成员都能够参加并同意进行变更。变更由程序管理来进行协调。
中间里程碑是建立在开发的每一个阶段内。这允许一个反复性的开发过程,在这个过程
中,多个部件可以被并行地开发,并且为了在中间里程碑上的测试和发布时的测试而被
集成,然后再被连续地、并行地开发。每一个组成员“掌握”着规定得很清楚的项目的
一部分,他们和组的其余人员对这一部分项目负责。
风险驱动的进度安排和版本发布有助于形成一种“产品意识”,在这种观念中,发布数
据通过互相协调来得到满足,为了以后的发布,将推迟有风险的特征和想得到的新特
征。

第二章设计指南

这部分是使用 Microsoft Visual Basic 企业版中的工具和技术开发基于部件的应用程
序的详细设计指南。
概念设计从应用程序的需求开始,需求是由规格说明和用法方案产生的。
逻辑设计将需求映射为抽象的业务对象和它们所关联的服务。
物理设计和部署进一步发展规格说明,将这些服务作为物理部件实现,并且将这些物理
部件分配到网络上适当的位置,以求达到高效、易维护并适应网络拓扑结构及带宽。
最后,提供了如何在分布式部署中实现 ActiveX 部件(原来的 OLE 服务器)的全过
程,其中还包括如何与“远程自动化”和分布式部件对象模式 (COM) 接口。
第一节 概念设计
 概念设计概念设计的重点在于:解决业务问题;获得满足用户需求的解决方案。这种
设计要求编写用法方案,用法方案体现用户对此业务问题的解决方案的意见。

概念设计作为开篇,引出了《客户/服务器应用程序开发指南》的“设计指南”部分,
这一部分推荐了用 Microsoft Visual Basic 企业版来建立大型的客户/服务器应用程
序的策略。
正如前面一些主题中指出的那样,单独一个客户/服务器范例并不能充分表达分散计算
的所有技术上和组织上的需求。基于部件的计算会有更广阔的前景,这种方法把大的应
用程序分解为独立的、更易于管理的“部件”,这些部件可以独立地开发和测试,并且
根据性能、安全性、可用性和维护问题,通过网络可以把它们分散开到不同的地方。
分布式计算要求一个统一的策略来规划、设计和实现新的系统。本章将介绍一些新策略
用于设计基于部件的方案。
开发分布式应用程序的策略必须从理解用户和业务的需求开始,就是说,要认清应用程
序的需求,这也可称之为概念设计。
主题

 一.基于部件设计的三个阶段这个主题介绍了基于部件的设计模型,并且纵览了概念
设计,逻辑设计和物理设计的要素。
术语“架构”和“设计”经常被互换使用。它们都是提出一个计划和安排,用来掌管事
物如何起作用或达到一个特殊的目标。一个微小的差别是:架构描述的是系统中的各个
部分组织并集成在一起的方法,而设计则提出所有要素及其细节的特定计划和安排。从
这点看,第一章所提出的基于服务的模型就构成了一个概念的客户/服务器架构,而这
一节的主题给出了基于这种架构的具体设计指南。
为了理解基于部件的方案设计,我们从三个不同的方面来看一看这个过程:
■ 概念设计
■ 逻辑设计
■ 物理设计
 最高级的视野是概念视野,然后是逻辑视野,最后是物理视野。在设计应用程序时,
即使在过程中各个阶段可能会互相重叠,通常每个阶段还是按顺序进行。
概念视野
建立应用程序都是为了解决业务问题,并且应该注意到一条重要的原则即业务需求决定
应用程序开发。在设计过程中的任何时刻,当前设计的情况必须直接来源于业务的问题
和需求。
为了达到这个目的,必须通过开发使用脚本来进行概念设计。这些剧本是用户解决特定
业务问题的直接表示。概念视野的重点是解决业务问题和导出满足用户需求的方案。
逻辑视野
逻辑视野直接从使用脚本中导出业务对象和相关的服务。这种方案的逻辑视野为评价不
同的物理选项提供了基础。也为项目组把方案形式化了。
物理视野
物理设计将业务对象和服务映射为物理部件。这些部件应该直接源于原始的业务对象和
使用脚本。物理设计也将集中于支持现存的基础和技术,使得风险最小,并且缩短开发
周期。
本章的主题集中于概念设计,“逻辑设计”集中于逻辑和物理设计
着眼于用户
概念设计是这样一个过程,它获得、评估和描述并确认用户想象的业务方案究竟是什
么。它指定了应用程序的用户和业务的需求,并引出一个业务方案,这个方案就象用户
看到的那样。
设计的这一阶段与物理实现是独立的。主要的焦点在于用户想干什么,也可以用他们想
如何干这件事来解释(但不是怎样去实现它)。
它基于方案行为的推演,并强调对用户的重视。始于对业务活动的重视,而不是软件开
发的各个方面,指出了这样一个事实,即系统存在就是为了服务于业务需求。在项目的
初期,对用户给予很强的重视将有助于在整个开发周期中保持良好的前景。
概念设计导致为了解决业务问题系统做什么的第一个描述,这些业务问题在构思/范围
文档中已经清楚地表达出来。

 二.概念设计过程这个主题概述了概念设计的要素,概念设计是一个反复的过程,其
目标就是定义用户的需求和应用程序的业务需求。
在核准构思/范围里程碑以后,概念设计就确立了基线。基线是一个临时的里程碑,它
被看作是一个参考点,而不是固定点。在基线以后,概念设计过程可以延续一段时间,
但是文档必须置于配置控制之下。
并行设计和反复
在概念设计中确认了业务方案部分,使用脚本也就产生了,这就将进入逻辑设计。逻辑
设计成果成为物理设计的基础。尽管这似乎意味着设计中的三个视野按照严袼的线性顺
序进行,但对全部三个视野的并行工作一般会提供更大的灵活性。采用同时工作的方
法,一种视野的成果可以平稳地过度到下一个视野中。
概念设计可能要经历几次反复,因为通过逻辑设计和物理设计,一些细节将陆续被加入
到功能说明中。如果要采用或需要一些要素,而这些要素在概念设计中没有包括,概念
设计必须重新进行。反复一直会发生,直到设计已经相对稳定并且它的正确性得到了普
遍的认可。从这点看,概念设计是在变化控制之下的。

 三.组的角色和职责依照客户/服务器开发方案的组模型,在概念设计过程中,每一个
组成员都要认清自己的角色。
项目组中的每一个成员都包括在概念设计中。虽然项目经理和产品经理负有主要的责
任,但组的其他成员也应参与确保一致性和有潜在危险的工作。每个组成员必须考虑任
务要求如何影响自己的责任。例如开发和测试/质量保证需要对业务方案的理解,用户
培训要求理解听众,后勤要求理解方案的地理分布情况。
组的角色   职责

产品经理   取得用户的输入和反馈;确保听取和理解用户的意见;掌管使用脚本的有
效性。
项目经理  拥有概念设计;驱动整个设计过程;协调有效性与企业架构相一致;并确保
平滑地过渡到逻辑设计。
开发人员  寻求业务政策声明中的完整性和一致性;考虑后面设计过程中可能出现的危
险 / 挑战;帮助评价现在状态和将来状态之间的差距。
用户培训  着眼于可用性问题;根据计划系统的使用脚本所隐含的变化程度,来评价将
来的培训和用户支持的要求;象用户的代理人一样。
测试和质量保证  考虑可测试性问题,象用户的拥护者一样。通过确认使用脚本来确保
系统与用户需求是一致的。
后勤人员   根据业务活动将在哪里发生,来考虑下部组织问题和首次上市的问题。

 四.设计成果概念设计过程的主要成果是:用户的轮廓、市场活动的当前状态的报告
和表达应用程序将来预期的状态的报告。
设计过程的最终结果就是一套文档,这些文档清楚地描绘了应用程序的背景、需求和实
现的计划。在概念设计中,文档格式的层次应该适应环境,并且应该是与特定听众交流
时效果最好和效率最高的方式。因此,这里所讨论的成果可以是任何层次的格式,从精
细的演示到简洁的、非正式的讨论,这要看环境和项目的复杂性。
基本上,概念设计过程所产生的三个结果是用户的轮廓,当前状态的使用脚本,和将来
状态的使用脚本。
用户轮廓
用户轮廓明确了用户是谁和他在业务中所处的角色。用户轮廓的作用是帮助项目组提供
使用脚本的内容。如果有这样的机会,即系统将改变用户轮廓,那么开发描述这种将来
状态的轮廓也是有必要的。
当前状态的使用脚本
现存系统的使用脚本记录了业务过程,就象它们现在执行一样。生成这些使用脚本是可
选的,尽管它们在暴露冗余的、低效的东西和隐藏的细节方面是很有价值的,而这些细
节在新的分析过程中可能不是那么显而易见的。它们也能为将来的开发提供参考点。
将来状态的使用脚本
为将来(或计划)的状态所作的使用脚本是所有后续设计活动的基础。这些使用脚本体
现了用户对业务的想法,并且尽可能详细地与这种理解相联系。对每一个业务活动(例
如处理顾客的付款)来说,通常将要为业务相关的每一个变化情况开发一个使用脚本。
例如,一个使用脚本可以描述如何通过支票付款,而另一个使用脚本则描述通过信用卡
付款是如何进行的。
当有新的需求时,必须更新为计划系统所做的使用脚本。

 五.设计任务指出了概念设计过程的五个基本任务,并且以循序渐进的方式加以讨
论。这些任务是按顺序列出的,但是开展过程却是反复进行的。
概念设计可以分为五个基本任务,伴随的结果是形成了一套使用脚本和用户轮廓,它们
记录了用户对业务问题的看法。由于新的要求不断提出和使用脚本要变得精炼,整个过
程可能反复很多次。项目组的责任和每一项活动所需要的技能之间存在着一个自然的对
应关系。下表推荐了概念设计活动和项目组角色之间的映射。
任务     包括的组角色

鉴别用户和他们的角色 产品经理(领导),用户培训(支持)
从用户收集输入   产品经理(领导),用户培训(支持)
撰写使用脚本   产品经理,用户培训
由用户认可   产品经理(领导),其他组成员(支持)
相对企业架构认可  产品经理

(一) 鉴别用户和他们的角色
在概念设计的内容中,术语用户实际上包含最终用户和业务要求。
传统的用户是指那些通过某一种用户界面而直接跟计算机系统交互的人。事实上,这种
最终用户只是在概念设计中需要考虑的几种用户中的一种。如果要求都是唯一地由最终
用户决定的,那么针对它所做出的应用程序在可行性类别上可能得分很高,但是必定不
能满足业务本身的战略的甚至战术的需要。
基于这种原因,概念设计中的用户可以包括顾客、提供者或者对解决业务问题有一定兴
趣的支持者。这种方法也认识到,不同用户之间可能有冲突的看法,他们在新系统应该
是什么样的问题上存在分歧。在设计过程的早期,核查不同的看法,就能成功地解决这
些不同的想法。
这种用来建立用户轮廓的信息将随着项目的不同而变化。然而,对所有的用户都必须阐
明下面的几个重要问题和几类信息。
■ 认知的需要
■ 工作的职责
■ 对当前系统的感觉
■ 物理因素
■ 心理因素
■ 经验

用户轮廓
用户轮廓的目的在于产生一个用户的精确图片,包括他们的态度和对现存系统的感觉,
以及他们对方案的想法。用户轮廓可以用来为产生使用脚本组织收集数据活动,或者用
户轮廓和使用脚本也能同时生成。
作为用户的业务
用户通常被认为是人类,这确实是多数情况。然而,一个用户也可以是一个部门甚至是
业务本身。在这种情况下也要开发用户轮廓;业务-象一个最终用户—具有可定义的兴
趣和目标。对业务的用户轮廓可以包含如下一些要素:
■ 组织/部门/单位的名称。
■ 使命、业务责任和活动。
■ 雇员的人数。
■ 从组织的立场看当前系统的感觉。
■ 从组织的立场看方案的认知需求。
■ 从组织的立场看术语、定义或者可适用的专门术语。

(二) 从用户收集输入
理解用户的各种技术可以分别应用,或者与其它技术一起应用。一些有用的数据收集的
技术,以及每种技术相对的优点和缺点,列在下表中。
技术    优点       缺点

采访用户   得到详细的、高价值的信息  时间集中;需要专业技能
联合应用程序设计 人所共知的;具激发性的   组的设置可能禁止了分享
(JAD) 期间
原型    产生经验数据而不是应答数据;  对确认很有价值 不可改变;
需要较高的经费支持
焦点组   获得详细的、高价值的信息  设置可能被禁止;高经费
用户调查   便于实现;得到的数据可迅速制表 并未与用户进行真正的联系;
难以抓住印象、态度
预示    提供在用户的环境中得到的  用户必须是可接近的
    很有价值的数据
向问讯处咨询  对用户经历的问题给出一个  离直接与用户联系还差一步
    直接的窗口
采访用户
接见用户是指项目组的成员和用户之间的一对一的见面。获得的信息的质量在很大程度
上取决于采访者的技能和被采访者的选择。采访可以成为其它信息收集技术的起点。
联合应用程序设计
联合应用程序设计,或者 JAD,是一个促进阶段,着重于单独的活动或者功能领域,在
这期间描述了各种各样的用户组。这个期间促进者不仅要研究完成什么任务,而且要研
究为什么要完成它们。典型地说,JAD 期间产生了一大批信息,这些信息必须被筛选并
且组织进入使用脚本。
原型
用户界面的原型使得用户在系统中可能见到的东西有效。原型要求对新系统如何运行有
一个稳定的概念。虽然原型所产生的信息量有很高的价值,但它可能需要专门的资源来
设计和建立原型。
焦点组
一个焦点组是从用户中抽选代表组成的,而这些代表的见解和态度是被受过训练的接见
者挑选出来的。这种方法能应用于这种情况,即用户太多以致不能直接包括进数据收集
的过程中。
用户调查
确定用户头脑中的东西,用户调查是最简单和廉价的方式。虽然用户调查能提供的信息
深度有限,但它能作为一些更强大方法的开始,比如采访用户或者焦点组。
预示
预示能把被动的观察同主动的访问用户联合起来。它典型地包括一个采访者,这个采访
者通过日常的任务来预示用户,从而来观察用户;这样获得的信息就是第一手的。同
时,鼓励用户尽可能详细地解释他的活动。观察者可以是被动的,仅仅观察和听用户
说,或者是主动的,当用户提供关于事件和活动的解释时直接地问问题。
向问讯处咨询
这种没注意到的但有效的技术在用户经历的实际问题上提供了直接的并且有价值的数
据。(这要假设存在应用程序的一个早期版本,而且正在被用户使用,这些用户由问讯
处所支持。)


(三) 撰写使用脚本
概念设计开始于使用脚本,而它们在用户的业务环境中抓住了系统的需求。在将使用特
殊的过程、特征或功能的环境中,使用脚本也描述了方案的需求。项目组可以利用使用
脚本来追踪对真正要解决的问题焦点。
在用户和项目组成员之间,使用脚本迅速地建立了一个系统的普遍理解,而且它还充当
了一个通用语言的角色,来表达参与到方案设计中的人的需求。
使用脚本服务是:
■ 将完成一个业务功能所需要的一系列步骤做成文档。
■ 在作者的想法和最终的物理设计之间描绘一条通路。
■ 对用户的期望添加真实性和焦点。
■ 在逻辑设计视野中为定义服务设立一个先决条件。
 不象缺少业务环境的一系列需求那样,使用脚本不仅用叙述性的或故事性的手法抓住
用户的看法和环境,而且也描述了必要的任务。在设计过程中,使用脚本使设计有效并
且有助于定义特征和进行折衷。
技术
使用脚本的表达和文档可以使用非结构的或结构化的叙述文字来完成,并且附以流程
图、使用脚本表、屏幕快照或者原型。
变化可以包括简单的叙述性文本和事件流程图,也包括有关一个活动的“谁、什么、何
时、何地、为什么和怎样”的表,还包括作了注解的屏幕快照或用户界面的模型。
通常直到逻辑或物理设计才开发功能原型;然而,它们可以用来理解和验证思想。在多
数情况下,一个单独的使用脚本将被以多种格式作成文档。在任何给定的情形下,任何
工作只要是能有益于用户和项目组之间的交流都将被采用。
使用叙述性文本的使用脚本
在下面的示例中,使用脚本示范了一个实例的结构描述,包括了 Book Sale 应用程序
所涉及的业务活动。这个过程简化为,假设只有一个店员完成应用程序所设计的功能
(为一个图书出版商计划卖书和收入)。
这个店员用电子表格半手工地从事必要的工作,下面给出了采访他所产生的叙述性文本
说明:
计划图书销售的使用脚本
“首先我从图书目录中选择认为重新出版可能会有利润的畅销书作者或题目。根据以前
的销售经验,决定用什么数学公式来计算新版的预计销售量。 接着决定什么生产选项
将影响重印所需要的成本 (COGS)。这些选项包括该书将采用平装还是精装,图画将采
用彩色还是黑白,还有将使用什么级别的纸张。然后估计做多少个月的广告,要花多少
广告费,还要估计第一个月的合理销售量。最后,我再考虑作者的版税和我们可能要付
给图书批发商的任何有促进作用的折扣。
“一旦将这些信息收集完全,我就把它们输入一个电子表格,并且对这些数据应用销售
量预测公式,产生计划整个时期的预计销售量。最后,我用电子表格来产生一个图表,
显示销售量与促销期间作者版税的关系。”
结构化的文本
结构化的文本提供了一系列行为的详细描述。它是从一个单独用户的看法来编写的。它
受一组先决条件和后续条件的约束。焦点在于用户想完成什么。详细的程度应当与描述
要实现一个预期的结果需要发生什么相适应。如何做咖啡的细节被省略了;重要的是做
成咖啡。
叙述性的文字和结构化的文本可以一起使用。虽然在详细程度和简单的容易产生的叙述
性文本之间存在折中问题,但是结构化文本中的顺序和详细程度可以超越叙述性文本所
包含的内容。这些细节的价值应该谨慎地去权衡。
 如何用结构化的文字产生使用脚本
1. 鉴别使用脚本。这将是一个功能或业务活动。
2. 通过角色鉴别用户(雇员、乘客、技术代表或者业务本身)。根据选择的用户看法
来编写。
3. 列出先决条件和后续条件。这是使用脚本将发生的边界。先决条件应该描述剧本发
生的特定环境。
4. 通过问“为了达到结果要求发生什么?”来写出步骤。答案应注重于什么而不是如
何。
5. 使用脚本中的一个单独步骤(例如“做咖啡”)可以分为它自己的单独的使用脚
本。这种分解抑制了细节,这些细节可能对某种用户(例如业务)不必关心,并且对确
认有很大帮助。

结构化文本的图书销售使用脚本
根据这些指导,早期产生的叙述性使用脚本能够转变为结构化的文本形式,正如下面例
子所示。
先决条件
存在一个可出版的书籍标题的数据库。
店员有对数据库的访问权。
店员选择要出版的图书。
步骤
1. 店员根据过去销售的经验选择一个销售预测模型。
2. 店员决定生产选项:
■ 平装或者精装
■ 彩色或者黑白
■ 所用纸张的等级
3. 店员决定促销时期和广告费用。
4. 店员指定付给作者的版税。
5. 店员指定促销的折扣和时期。
6. 店员将所有数据输入电子表格。
7. 店员运用从上面的步骤 1 中的公式来计划整个促销期的销售量和版税费用。
8. 店员使用电子表格结果产生销售计划图。

后续条件
保存销售量对版税的图表,用于复查的管理。

(四) 确认设计
产生使用脚本,或直接综合进评价用户输入的过程中,随后进行用户确认。不管它什么
时候发生,用户是否同意使用脚本精确地表示了对业务方案的看法,是项目生命期中的
主要事件。
确认的目的并不是向用户宣布项目的结束。它还应该揭示任何错过的部分和对用户期望
的不正确解释。在方案的某一个方面上用户之间的冲突观点也将变得非常明显,并且必
须立即竖起一个危险标志。
如何确认使用脚本
一个指导用户确认的有效方法就是贯通每一个剧本。在这个过程中,指导者可以问问
题,按照这种方法决定用户是否确认了个别的活动和事件。
正如使用脚本本身经常通过观察用户进行一项工作而得到阐述那样,验证可以通过扮演
角色得到指导。这种方式有它的局限性,但它非常有助于让用户看见方案。
另一个验证的方法是通过原型。原型主要着眼于测试用户界面,并且在较小的范围内它
可以用来验证业务过程的再设计。
在 Book Sale 这个示例中,早期给出的使用脚本可以用图 5.1 所示的用户界面原型来
验证。
基线里程碑
正如对客户/服务器设计的过程模型中的多数活动那样,目的就是尽早建立一个基线,
但冻结却是越迟越好。概念设计的基线是随着揭示了附加信息的后续设计阶段而调整
的。
当用户和项目组之间就需要什么和那些需要要求什么行为达成相互之间的理解时,概念
设计就可以建立基线了。

 六.一个基于部件的例子:图书销售把概念设计的原理关联到一个具体的例子上,引
出一个为出版业设计的实例应用程序。
本章和下一章用图书销售这个应用程序示例,给出为客户/服务器应用程序设计指导的
具体例子。现在这个应用程序的基本功能已经给出,编写这个应用程序并安装在机器
上,这样有助于对它的范围和功能有一个清晰的印象。当给出了应用程序设计所需的逻
辑和物理的指导后,这将变得特别有价值。
安装图书销售示例
可以在安装的 Visual Basic 中的 \Samples\Clisvr\Booksale 子目录中找到 Book
Sale 这个例子的全部文件。这些文件包括:
Book_cli.vbp
■ Book_cli.frm
■ Frmchart.frm
■ Frmcogs.frm
■ Book_cli.bas

Book_svr.vbp
■ Book_svr.frm
■ Model.cls
■ Sales.cls
■ Taxes.cls
■ Book_svr.bas

为了安装这个 Book Sale 例子的应用程序以便远程应用,使用应用程序 SetupWizard
来建立服务器部件所需要的安装盘 (Book_svr.vbp)。
一旦正确地安装并注册了部件,就从开发它的机器上运行 Book_cli.exe 应用程序来测
试。
测试图书销售
当运行 Book Sale 应用程序时,看见的第一个屏幕是 Book Revenue 主窗体
(Book_cli.frm),如图 5.1 所示的用户界面原型。也将看见服务器窗体(Book Sales
Server,Book_svr.frm)打开了,在本地机器上或者在安装的远程机器上,它已被客户
调用。如图 5.2 所示的窗体。
图 5.2   图书销售服务器窗体

在 Book Revenue 窗体的上部有两个列表框,允许选择书名和作者。随后,通过单击下
面的三个大按钮中的一个选择销售量预测模型:
 选择销售常年稳定的“flat”销售模型,例如 Bible 或者 Gone with the Wind。
 选择“mainstream bestseller”销售模型,销售量相对增长很快,接着就慢慢稳定地
下降。
 选择“celebrity book”销售模型,适用于书的主题在短时期内会引起浓厚的兴趣。
它表示销售量非常快地增长,一个短时期的高销售量,和迅速下降。

下一步,单击紧挨着“COGS”下拉列表框的“Browse”按钮,显示出“COGS”选项对话
框,如图 5.3 所示。
图 5.3   “COGS”选项对话框


选择希望的“Binding Type”、“Picture Colors”和“Paper Type”选项,接着单击
“Save”来记录选择,然后回到主对话框。
输入近似的作者版税的百分比(通常是百分之十和二十之间)以及提供的促销折扣。
最后,单击“Execute”按钮来计算销售和版税计划,并且用图表的格式显示出来。
逻辑和物理设计
本章介绍了概念设计的基本原理。下一章将介绍逻辑和物理设计的基本步骤,同时还有
具体的设计指导和实例,说明 Book Sale 实例应用程序设计中的这些原理。
示例应用程序
 Booksale.vbg本章的许多概念都在 Book Sale 示例应用程序中演示。该示例的文件可
在 Visual Basic 安装目录的 \Samples\Clisvr\Booksale 子目录中找到。关于建立和
运行该应用程序的详细信息,请参阅“一个基于部件的例子:图书销售。”


第二节 逻辑设计
逻辑设计逻辑设计直接从概念设计产生的用法方案中导出业务对象和它们所关联的服
务。逻辑设计为开发小组选定方案,并为评价不同的物理选项提供一个基础。

本章是“概念设计”的继续,它为构造基于部件的客户/服务器应用程序提供逻辑设计
的设计指南。它介绍了设计过程的第二阶段:逻辑设计,为了对这个阶段的工作加以解
释,它使用“Book Sale”应用程序作为例子。
在逻辑设计阶段,需要将概念设计阶段得到的使用说明书中的需求转化为业务对象和它
们提供的服务。
在逻辑设计的初始,将应用程序视为一个系统。系统的定义包括它的功能划分和它包含
的业务对象。逻辑设计阶段需要说明系统与外部实体(例如用户或其它系统)之间的接
口。一个系统内可能包含很多的子系统,因此还要说明这些子系统是如何划分的。
逻辑设计应该尽可能地独立于具体的技术,从而把系统的行为与它的具体实现分开处
理。只有当项目开发组确认逻辑设计已经体现了系统的基本行为之后,我们才可以考虑
技术上的限制条件。在透彻理解系统并编写了良好的文档之前,不要急于确定技术方
向。
主题

 一.逻辑设计小组的任务描述了各个开发小组在客户端/服务器开发工程中的任务,以
及他们在逻辑设计阶段的特定任务。
如“开发策略”所述,程序管理组负责总体的功能说明工作,这当然也包括了逻辑设计
的工作。开发组和Q/A测试组与这一阶段的关系比较密切。产品管理组、用户培训组及
后勤组与此关系不大,但是逻辑设计为这些小组提供了用于讨论的参考框架,小组还可
以以此为根据决定自己需要做的工作。
小组   在逻辑设计阶段的任务

产品管理组 确保服务精确地反映了用户看待系统的方式。
程序管理组 管理总体的设计过程;与其它组的成员进行协调。
开发组  参与业务对象与服务的提取与定义。确保深入到足够的细节。
用户培训组 使用服务和业务对象,编写用户培训材料。
Q/A 测试组 检查服务定义的完整性与一致性。确认整个逻辑设计是可测试的。确保逻
辑设计的有效性。
后勤组  在现有技术和设施的基础上,评估实现服务的可行性。

详细信息   关于小组的任务和责任,请参阅“开发策略”。

 二.逻辑设计的步骤在逻辑设计阶段有五个步骤。在完成应用程序的逻辑设计之前,
需要反复循环执行这些步骤。
逻辑设计阶段包括下面的五个步骤。尽管通常按顺序说明这些步骤,但是,请记住它们
是循环反复进行的;就是说,在完成较后阶段的步骤之后,需要对前面的步骤进行进一
步的优化。
下列的主题简要地讨论了逻辑设计的每一步。
■ 确定业务对象和服务   业务对象和它们要提供的服务都脱胎于概念设计阶段的使用
说明书。我们可能需要使用形式化的分析和设计技术。如果不用形式化方法,仅仅使用
直截了当的名词-动词分析,也能提取出几乎所有的业务对象和服务。
确定业务对象最直接的方法是:分析使用说明书中的名词。一般来说,如果名词代表了
系统或业务感兴趣的东西,那么它极有可能就是业务对象。
使用说明书的主语通常是业务活动中的个人(例如在旅游事务中的职员或代理商)。它
也可能是一个组织、另一个系统或正在开发的系统。如果满足下面两个条件之一,那么
名词应该被视为潜在的业务对象:
1. 是系统需要了解的事物(例如旅客)。
2. 是系统赖以获得信息或提供信息的物或者人(例如预定代理商)。这可以从系统的
描述图中判断出来。

详细信息   关于使用说明书,请参阅“概念设计”。关于业务对象和服务的详细说
明,请参阅“服务模型”。

“Book Sale”示例中的业务对象
根据“概念设计”中为“Book Sale”应用程序写的使用说明书,可以提取出表示业务
对象的名词,如图 6.1 所示。
图 6.1   “Book Sale”示例应用程序的业务对象
   书       促销





  预测模型      数据库






根据使用说明书确定服务
使用说明书中的动词是最好的服务指示器。虽然并非使用说明书中的每一个动词都可以
直接成为逻辑设计中的服务,相当多的动词确实是表示服务的。
在逻辑设计阶段,必须清楚地了解步骤或动作所在的上下文环境。例如,在“Book
Sale”使用说明书中,职员需要查询书名和作者。这是一个用户动作,而不是系统主动
执行的,需要知道的是:在发生这个动作时,需要系统作什么?很明显,在这个例子
中,系统要提供的是“得到书名”和“得到作者”服务。
下面是确定服务的一些原则:
■ 确定操作是否在系统范围内。如果不是,那么系统是否有责任帮助实现这个动作?
■ 确定业务对象,它应该是动作的接受者,或者负责执行动作。
■ 仅使用及物动词。

“Book Sale”示例中的服务
利用“概念设计”创建的使用说明书,我们从中提取动词,为“Book Sale”应用程序
确定了下列服务:
■ 得到作者
■ 得到书名
■ 得到商品价格
■ 得到销售模式
■ 得到推销日期
■ 得到作者版税
■ 计算销售预测
■ 计算版税预测
■ 显示图表
 随着对象和服务的进一步优化,该列表可能会发生改变,但是它为后面的物理设计和
部署阶段提供了一个良好的开端。

■ 定义接口   服务通过接口提供它的功能。在逻辑设计阶段,服务接口被定义为供应
者服务和消费者服务之间的协议。在公开了接口之后,就不要再对它进行修改,从而能
够避免破坏外部的相互依赖关系。
正如在“确定业务对象和服务”中所述,在确定了服务以后,就应该定义它们的接口
了。接口需要清楚地说明调用服务时需要的前提条件,以及它的调用约定(包括语法、
输入参数和输出参数)。
例如,在“确定业务对象和服务”中,我们提取出“得到书名”服务,它的接口的简化
版本可以写成下面的样子:
前提条件
■ 需要一个出版商数据库,其中保存了出版商的库存中的书名。
■ 某些书是可以出版的,数据库能够将这些书与不能出版的书加以区分。

调用约定
GetTitles (Status)

其中,Status 值为 Available 或 Unavailable(表示能够出版)。
输出(Output)
当前可以出版的书名清单。
用户接口的定义必须足够的简洁,方案的构造者只需要将服务看成“黑盒”,他们可以
对服务的内部实现方式一无所知,只需能够调用服务并得到需要的结果。这就简化了服
务的使用(以及复用),可以在不影响用户的情况下进行维护和性能改进。
在开始实现接口之前,应该先定义并公布所设计的接口。部件的开发者将以此为实现依
据,需要使用该服务功能的其它组的成员也可以以此来使用它(甚至在接口真正实现之
前)。一旦接口被公布于众,它就成为服务方与它的客户方之间的协议,它的底层实现
可能随着时间的推移而进行了很大的改变,但接口是不能被修改的。

■ 确定业务对象的相互依赖关系   当业务对象的服务调用了另一个业务对象的服务
时,就出现了业务对象之间的相互依赖问题。尽管在逻辑设计阶段出现相互依赖是很自
然的,但是,如果业务对象之间的相互作用跨越了子系统甚至系统,问题就比较麻烦
了。
确定业务对象之间的相互依赖关系是一门艺术,几乎没有什么固定而且快速的规则可
用。相互依赖关系可能反映了对象的内在特性(例如“订单”的前提是“顾客”,并且
依赖于“顾客”的存在),也可能反映了每个对象提供和消费的服务的具体细节。
通常,如果业务对象需要执行业务事务,或者需要协调其它的业务对象,那么它们将比
没有相互依赖关系的业务对象更容易受到需求变化的影响。因为业务对象的相互依赖关
系隐含着一定程度的耦合,所以在实现时应该经过深思熟虑。
首先试图确定相互依赖关系,只要有可能,可以重新规划业务对象以减少相互依赖关系
的数目,并把可能频繁改变的服务从相对稳定的服务中抽取出来。例如,计算预期利润
和损失的规则是可能频繁变化的,而用来提取价格和用来进行预测销售数据的规则是相
对稳定的。按照这种分类方法,预计模型对象中的公式与销售推销对象中的商品价格应
该分开。
虽然在逻辑设计阶段这是一个高度抽象的过程,但是,它对于逻辑设计的具体实现有着
非常重要的意义。因为设计的最终目标是:设计出尽可能自包含的部件,只有这样,一
个服务发生变化时才不会导致修改多个部件。

■ 逻辑设计的检验   逻辑设计必须与使用说明书进行对比,以确保概念视图中的需求
已经被完全、正确地反应在逻辑视图中。
使用说明书中的每一种操作都必须在逻辑设计中有所体现。对于使用说明书中的每一种
操作,至少要有一个服务与之对应。
正确性
逻辑视图中的行为应该等价于概念视图中表达的行为。在这一步,任何的偏离都是对用
户需求的偏离。通常需要进行系统的、全面的检查,以验证逻辑设计的正确性。检查的
方式是全面地查看使用说明书,确保定义的对象和服务能够满足它们的要求。
清晰性
另外一点也是至关重要的:保证定义的业务对象和服务没有二义性。通常,这要求逻辑
设计必须是非常简洁的。逻辑视图的表达是否清晰并不会直接影响到使用说明书的验
证。但是,如果逻辑设计不清晰,进行完整性和正确性检查是很困难的。

■ 逻辑设计的修改和优化   只经过一轮循环不可能产生完整的逻辑设计。工程组必须
在逻辑视图上进行多次的循环反复,每次都增加一些新的细节。
利用使用说明书的名词确认业务对象确实是一种快速的好办法。但是,这种方法得到的
业务对象通常是不完整的和/或是不正确的。要使设计方案更加完善,需要注意以下几
个问题。
第一,不是所有的名词都与问题的解决方案有关。留下来的候选名词包括一些物理实
体,如顾客、雇员和设备,以及一些指示业务事务的概念,如房间分配、付款核准和推
销等。需要删除的候选名词具有以下特点:
■ 无关性
 如果业务对象与方案无关或是超出范围,可以删去它。
■ 冗余性
 如果两个业务对象表达了相同的信息,或控制了相同的活动,那么可以把他们合并为
一个,可以选择含义最明确的名称,或者修改对象的名称。
■ 意思含混
 业务对象必须指代明确。某些候选词涉及到使用说明书之外的内容。这些业务对象并
不一定不正确,但是通常应该使它们更具体一些。
■ 从属属性
 使用说明书中的某些名词是其它业务对象的关键属性。如果某个属性在方案中必须独
立存在,那么需要将它作为一个独立的业务对象处理。
■ 事务和控制
 如果一组服务必须以某种方式进行协调或控制,那么这组服务应该由一个独立的业务
对象提供。
■ “演员”和“角色”
 某些名词并不代表任何实际的业务对象,而只是系统中的“演员”而已,或仅仅是业
务对象的职能,而不是业务对象本身。

 三.逻辑设计的基准如果所有的小组都对照概念设计的使用说明书检验通过了逻辑设
计,那么逻辑设计阶段的任务就完成了,逻辑设计将成为后续开发的基准。
如“开发策略”所述,整个的设计过程是循环进行的,在开发循环中要不断地进行优
化。按照本章提供的步骤进行的逻辑设计并不是最终的结果,它只是一个基线,它是一
个文档化的出发点,最大程度地适应当前开发阶段已理解的应用程序的需求,并且得到
各个工程组的一致同意。
要使逻辑设计称为基准,关键在于按照概念设计阶段设计的使用说明书对它进行检验。
下表中列出了各个开发组与基准确定有关的任务。
工程组  基准确定规则

产品管理组 确保逻辑设计和概念设计完全对应。
程序管理组 检查工程组之间的协同。保证在逻辑设计中成员之间的信息通讯畅通。
开发组  检查服务和接口设计的完整性,以及对业务对象的封装是否合适。
用户培训组 检查可用性方面的问题,尤其是用户接口支持的用户定位和查看的类型。
测试/QA 组 保证系统是可测试的,即使是在逻辑设计阶段,并且保证系统的行为与用
户想象的一致。
后勤组  检查可能出问题的基础设施、支持或网络问题。

逻辑设计中的风险因素
必须严格依照“逻辑设计的步骤”中列出的逻辑设计步骤进行设计,只有这样,才能保
证设计出来的应用程序逻辑视图能够精确地反映需求,能够有效地实现基于部件的方
案。如果遵照了这些步骤,而且工程组的成员都按照上面的列表尽职尽责地工作,并且
同意将逻辑设计作为下一步工作的基线,那么取得成功是相当有把握的。
然而上面的过程中也存在若干风险因素,这是需要引起注意并加以避免的。主要的风险
包括:
■ 如果服务表示的行为是使用说明书中没有的,或者没有全面地反映使用说明书的内
容,那么概念视图与逻辑视图就成为不一致的。这是最主要的风险。
■ 如果逻辑设计阶段没有认真考虑到与企业现有的总体结构相适应的问题,那么,由
于不能遵循企业标准,软件可能无法集成到企业环境下。
■ 如果逻辑设计严重地依赖外部系统,那么系统将很容易受到外部系统改变的影响。

第三节 物理设计和部署
 物理设计和部署物理设计将业务对象和服务映射为物理部件。它调节已存在的基础结
构和技术尽可能降低风险并缩短开发周期。物理部署考察在网络上分配这些部件的策
略,权衡利弊,使其具有良好的可扩展性和适应新的拓扑结构的能力。
本章将继续讨论在“概念设计”中介绍的基于部件的客户/服务器应用程序的结构设计
问题。在这里除了介绍最后一个设计阶段物理设计和部署之外,还介绍了如何计划和实
现网络上的应用程序部件最后部署和性能测试的方法。
主题

 一.物理设计的目的物理设计是客户/服务器设计过程的最后一步,在这里着眼点是从
系统的抽象行为转向系统的物理实现。
物理设计的目的是根据性能、管理以及开发过程的需要把逻辑设计翻译成可以有效实现
的解决方案。即使遇到技术上的限制,这种物理意图也应该能够正确地实现所需要的系
统行为。
在物理设计中,设计重点从系统行为的抽象转向了行为的实现。由于逻辑设计大体上是
和技术独立的,因此物理设计就必须和选定的技术条件相结合,这些技术条件指应用程
序所在的硬件和软件环境。
物理设计的目标,是确定如何从软件部件建立起应用程序的每一部分。这些部件通过定
义接口的相互作用,就能够从总体上实现系统所需要的行为。部件之间的通讯规则,是
通过交互标准定义的:部件的功能和它的通讯方式,是物理设计中要着重考虑的。

 二.物理设计中的团组角色介绍了在客户/服务器开发工程的物理设计阶段中各个团组
所担任的角色,和他们在逻辑设计过程中的特殊职责之外。
程序管理组控制着功能的指定,因此也就控制着物理设计。执行物理开发的主要职责在
于开发组。测试与质量保证 (QA) 组在物理设计过程中也是很重要的角色。
团组角色  在逻辑设计中的职责

产品管理组 保证设计能够充分表达原始使用方案中要求的所有用户需求。同时以征询
用户需求的态度来传达设计过程中的结果。
程序管理组 推动整个设计过程。保证企业结构中的相互关系以及各种折衷情况能够得
到表示和传达。同时在物理设计中保证逻辑设计的完整性能够得到维护。
开发组  物理设计的主要力量。全面规定出部件的包装以及各自的服务。
用户培训组 为用户团体和必要的支持/操作人员计划培训日程。
测试和 QA 组 在设计中保证部件的全面性和一致性。计划物理代码测试过程并保证开
发组提出的部件是可测试的。与产品管理组一起保证设计充分满足使用方案的需求。
后勤组  了解解决方案所需要的技术环境,制定适当的交货和安装计划。与企业中的各
个操作组交流计划和需求。

 三.物理设计的过程对于从一个定义好了的逻辑设计转化成软件部件的真实的物理设
计,提供了所需各个步骤的概述。
部件的包装、分发以及接口的定义是物理设计中的关键活动。
把一个定义好的逻辑设计(业务对象和服务),转化成一个已实现的物理设计(软件部
件),其过程包括下面这些步骤:
1. 向部件分配服务- 根据所包含的服务,把每个逻辑业务对象翻译成用户、业务或者
数据服务部件,这样就可以获得一套初步部件。
2. 在网络上部署部件- 把每个部件都分配给网络上的一个节点。
3. 优化部件的包装和发布- 划分部件并将其分组,以反映技术上的限制。
4. 指定部件接口- 在指定的部件之间实现供应者和消费者关系的机制。
5. 验证物理设计- 物理设计在总体上必须和逻辑设计一致,也必须和企业整体结构一
致。验证过程保证了每个部件服务都可以直接追溯到逻辑业务对象中的服务。


 四.向部件分配服务对应用模型中服务的划分,可以得到应用程序中物理部件的初步
设计。
应用模型中服务的划分,可以得到一些初步部件。在这个阶段,部件仍旧是抽象的,也
就是说它们还不能反映物理实现上的限制。
什么是部件?
部件是一个或几个服务在物理上的封装,可以通过它的接口获得这些服务。它可以是
.exe 或 .dll 文件,数据库触发器和存储过程的集合,或者任何几个其它物理软件实
体。部件是由它所提供的服务以及它和其它部件的相互作用来定义的。部件的内部结构
和实现,对于外部世界是隐避的,这就是说,部件也具有业务对象的模块化和封装特
点。
关于部件,外界所能知道的就是它的接口。不需要考虑供应者部件是如何以及在哪里实
现,“消费者”部件就可以调用供应者部件的服务。如果企业中有了公共的交互标准
(比如 ActiveX 和分布式 COM),那么要促进部件的通讯就可以不需要特殊的连接软
件了。
直接从业务对象导出部件
在一开始向部件分配服务的过程中,服务可以根据业务对象和服务类型进行分组。从单
独一个业务对象开始,对象中的服务可以分成用户、业务,或者数据服务。这样,根据
所包含的各种服务类型,服务就可以组织成独立的部件了。
这种方法创建的初步部件集,保留了应用模型的抽象含义,而一般情况下,最终的部件
会有很高的内聚性和模块性。
部件的初始分布
这种在应用模型上的划分提供了一个很好的基础,根据该基础就可以决定把部件分布到
一定的物理位置上。
提供用户服务的部件,可以分配给桌面上的机器,数据库部件,可以分配给所用的数据
库服务器,而提供业务服务的部件,则可以分配给单独的业务服务器。
这些原始的分配很可能因为性能、开销以及其它部署上的考虑而发生改变,类似的原因
还包括以后优化了的部件的定义。这时,再没有实现上的限制存在了。

 五.在网络上开发部件讨论了网络性能和实现上要考虑的因素,以便决定在哪里部署
应用程序的各个部件。
在制定部件的部署决定时,应该考虑的因素包括:
■ 引用的本地化
■ 部件的互操作性
■ 数据分布问题
■ 交互界面
■ 安全性
■ 所有权
■ 可靠性
■ 可支持性

“引用的本地化”意思是把部件放在使用它的地方,使网络流量最小。
理想情况下,本地化的决定应该从业务和操作需要中产生。但实际上,经常会由于采用
的技术而产生各种限制。利用基于服务的应用模型导出部件,就有可能获得独立性程度
比较高的本地化结果。
一旦引用的本地化已经决定,那么就应该考虑影响分布的技术因素和操作因素了。这包
括性能上的限制、网络带宽、是否有能力支持目前所需要的用户数量、维护、管理、强
健性以及安全性。
网络性能和较正
分布式应用程序的设计必须考虑网络的使用情况。对网络要求太高的应用程序很有可能
无法使用。因此在设计网络应用程序时必须比较现实地使用网络。
对于多用户系统,同时连接用户的总数是一个重要的因素。但这只是衡量任务吞吐量的
因素之一,吞吐量还与 CPU 带宽、存储能力、网络和输入/输出带宽,以及数据传输需
求等因素有关。显然,部件的部署应该使网络调用和通过网络传输的数据量都达到最
小。
另外要考虑的一个有关网络的问题是“校正”,这是指如何更好的使网络服务于竞争应
用程序,其中包括这些问题,死锁的可能性、同步、资源的公平分配、协议的兼容性以
及安全性等。
在这里不可能对所有这些问题都进行详细地讨论。其中很多问题和它们的实现方法一
起,在“实现 ActiveX 部件”中讨论。

 六.优化部件的包装和部署为精确谐调原始部件的定义,提供了准则和方案,同时还
讨论了不同的物理部署选项、实例模型、接口以及执行模型的优劣。
在做出初步包装和分布决定之后,下一步就该是优化物理设计了。这种优化应该着重于
优化三种主要的物理约束:
■ 粒度- 部件应该多大?
■ 可重用性- 部件如何包装才能被更多应用程序使用?
■ 包容和集成- 各个部件块应该怎样汇集起来?

本阶段设计优化过程中要考虑的主要因素之一,是满足系统的性能需求。应对每个节点
完成分配功能的能力进行仔细估计。另外,还会遇到其它的问题,比如可靠性以及是否
有能力实现设计。下面这些问题为优化物理设计提供了一个良好的开端:
■ 节点是否有足够的处理容量、满足客户请求的频率并以最大的处理能力来执行服务

■ 对机器之间参数和结果集传递所做估计的极限是多少?
■ 在提出的分布方案中,需要多大的网络带宽才能充分支持通讯?
■ 部件的相关性是否在进程之间进行了分布?在一个节点或网络上的故障会有多大的
影响?
基本准则
下面是一些关于制定部件包装和决定分布的基本准则:
■ 部件应该有很强的内聚性。经常在一起调用的服务(在同一业务事务中)应该划分
在同一个部件中。
■ 部件之间的耦合程度应该较低。任何部件中的服务不应该依赖于其它部件中服务的
实现细节。
■ 部件应该能分布到使用它们的地方(引用的本地化)。
■ 不在一起调用的服务,应该放在独立的部件中。
■ 部件包装使用的方式,应该能够提高它在其它上下文中使用的可能性。
■ 在以前创建的部件或能够买到的部件中,可能已经实现了一些所要求的服务,应该
把它们标出来。

设计良好的部件模型(松耦合性,强内聚性)的优点之一在于,如果分布需要某个部件
产生变动,那么这些变动就可以结合到部件中(或者用新的部件替换),并且对系统中
其它部件的影响能够最小。
粒度
部件的大小是可以度量的。于是问题就产生了,“一个部件应该多大?”
部件可以很小、很大或者不大不小。从小的方面讲,简单的部件可以提供有限的几种功
能,比如验证国际地址中的邮政编码。从大的方面讲,复杂的部件可以提供高级的信用
检查、分期偿还,以及委托授权等功能,甚至包含几乎整个应用程序。
在决定粒度的大小方面,主要应考虑的是,很小的部件可能有比较好的内聚性和通用性
- 它们一般只做一件事,因此更容易维护,而且比起大的、复杂的部件来有更好的可
重用性。但另一方面,如果网络上有成百个很小的部件的话,也会带来很大的寻址和维
护问题。
最优粒度大小,一般是在两种极端情况之间。
可重用性
如果在部件级可以重用代码的话,那么就可以获得最大的效率。
重用的好处很多,不只是部件的粒度。对于每个部件,应该考虑一下“这个部件是否独
立同时又可被其它应用程序使用?”
在该模型中有两个实现重用的机会。首先是重用低层次的代码(函数或对象)。要做到
这一点,应该清楚地表达业务对象的数据和逻辑,这样开发者就可以在一定程度上了解
业务对象内部的工作情况了。这一级的重用可以叫做源或代码重用,通过使用类模块可
以很好地表达这个意思。
重用的第二个机会是在部件级上的封装。部件层次的重用有一些显著的优点。部件是被
当作黑匣子对待的。在这种情况下,开发者只能看到接口。但是,接口一旦定义好了,
对于部件来说它就不能改变了。正是对接口的这种严格定义才使得部件能够被有效地重
用。
同样,采用多大的粒度把服务包装进可重用的部件,仍旧是一个问题。采用最高级的粒
度(部件最小),可以达到普遍重用的最大可能性,但如果把这些小块组装成有用的高
级组件有很大困难的话,那么就没有什么潜在优势可言了。
包容和集成
在物理部件定义中的一个重要问题是部件要如何组装和使用。部件是整个自包容的,并
且只对它们自己的数据实例进行操作。但是,对于部件来说,互相合作以实现更大的功
能往往是必须的。例如,一个“客户发货清单”部件,可能需要调用一个“客户”部件
和一个“销售”部件。这里需要有一些包容或集成的概念,就是使较大的部件无需重复
编码能够“包容”低级部件的功能。
包容把内层部件的接口安排成只对外层部件可见。如果其它部件想要使用内层部件的功
能,它们必须调用外层部件,后者依次调用内层部件来提供所需要的服务。
另一方面,集成把内层部件的接口直接暴露给“外面”的世界。
包容是一种比较常用的实现方法,因为它提供了更强的灵活性,但在某些情况下,如果
要“穿过”一个部件去频繁调用另一个部件,就会有额外开销,因此,集成的效率可能
更高一些。
(一)部件部署选项
下图说明了 ActiveX 部件部署的五种基本形式,它们都可以从 Visual Basic 的类模
块得到。
部件部署选项



 1.内部       2.进程内外部本地     3.进程外外部本地



 4.进程外外部远程      5.进程内外部远程

1. 内部
  在这种情况中,类模块的源代码被集成到主要工程,并被编译成单独一个可执行文
件。对于访问类的接口(方法和属性)来说,这种方法提供的性能是最高的,但在类模
块更新之后,需要重新编译整个应用程序。这种方法还能减小二进制(编译过的)对象
的粒度,因为对象是和应用程序的其它部分一起部署的,这就限制了对象的可重用性。
2. 进程内外部本地
 在这种情况中,类模块的源代码被编译成独立的二进制库 (DLL),并作为进程内部件
加载到主应用程序的进程中。这种方法提供的对部件接口的访问性能和完全集成的内部
形式几乎一样,同时这种方法提供的包装方案还允许更新部件而不必重新编译主(客
户)应用程序。由于进程内部件是独立的二进制文件,因此和编译进原始的主应用程序
比起来,可以更有效地被其它客户应用程序重用。
3. 进程外外部本地
  在这种情况中,类模块的源代码被编译成独立的进程外 (.exe) 二进制部件,并被驻
留在独立的进程空间内的客户应用程序调用。这种方法提供的对部件接口的访问性能比
起前两种来要慢一些,但这种方法允许部件在它自己被保护的进程空间里以它自己独立
的执行线程独立运行。如果部件同时被多个应用程序共享或者部件执行需要很长时间的
任务,而调用它的客户应用程序在完成之前又不想阻塞,则这种独立性十分有用。(异
步的部件接口在本章后面的“使用回调的异步执行”中有更详细的讨论。)
4. 进程外外部远程
 在这种情况中,类模块的源代码被编译成独立的进程外 (.exe) 二进制部件,并被驻
留在一台独立的机器上独立的进程空间内的客户应用程序调用。这种方法提供的对部件
接口的访问性能,比前面三种情况都要慢,但它使部件能够利用功能更强大的外部共享
服务器的带宽和资源。由于这种方法的部署模型是有约束的,因此用这种方法把部件放
在集中的系统上还可以极大地简化更新和管理任务。这种模型还可以使部件能够被多台
客户机上的多个客户进程并发共享。
5. 进程内外部远程
  在这种情况中,类模块的源代码被编译成一个独立的二进制库 (.dll),并作为进程
内部件被加载到远程的主机进程中。虽然远程的客户应用程序必须间接地从主机进程接
受对进程内部件的引用,但在这之后客户应用程序对部件的所有引用就全是直接的,同
时访问性能也就和进程外的远程情况差不多。这种模型是建立在进程外远程基础上的,
但它的好处是,运行部件不再需要加载整个进程。这可以大大地减少连接时间和运行多
个部件时所需要的操作系统资源,同时,与每个部件都需要自己的进程空间这种情况相
比,开发者开发的部件就可以更小。
(二)部件实例选项
如下图所示,对于实例化公共的可创建的 ActiveX 部件,Visual Basic 提供了两种主
要选项:多用途 和 单用途。这些选项设计时可以在类模块属性工作表的
“Instancing”属性中指定。
MultiUse 和 SingleUse 实例选项





1.多用途        2.单用途

1. 多用途
在这种情况中,创建对部件引用的第一个客户加载并初始化部件。以后所有的客户应用
程序如果要创建对部件的引用,就都会收到一个指针,指针指向同一个已创建了的实
例。这就是术语“多用途”的含义。在这种方式中,资源的使用达到了最小,因为操作
系统只需要支持一个服务器,但客户在服务器完成前一个请求之前必须一直阻塞。如果
服务器为其客户应用程序执行的任务很快,那么这会是一种很有效的模型。但是如果任
务需要很长时间,那么这种模型就会成为应用程序中的瓶颈。(注意:客户的阻塞和重
试是在超时参数超过时由 COM 管理的,这时客户会从它们试图进行的访问那儿收到一
个错误。缺省的超时时间值是 5 分钟。)
 如果部件没有可视的界面,并在编译时被标成了无用户界面执行,那么它就可以作为
多线程部件运行,给每个请求的客户一个执行线程。这可以消除上面所说的客户阻塞。
关于多线程和无用户界面执行的详细信息,请参阅“实现 ActiveX 部件”。
2. 单用途
  在这种情况中,每个要创建对部件引用的客户都要加载和初始化一个新的部件的物理
实例。在这种方式中,每个客户都会以它自己在服务器上的被保护的进程空间和独立的
执行线程,获得对部件的访问能力,并因此保证了各个部件能够并发地执行,而不会被
其它客户阻塞。但是设计者必须记住,在负载过重的情况下这种模型对资源的要求可能
会产生昂贵的代价。

详细信息   关于部件实例的详细信息,请参阅《部件工具指南》中“部件设计的一般
准则”的“ActiveX 部件提供的类的实例化”一节。
(三)使用回调的异步执行
如下图所示,Visual Basic 提供了部件异步执行的能力。在把大的计算任务卸载到远
程机器上的同时,又要保持用户界面能够得到响应,那么异步执行部件就是一种很有价
值的机制。对于不需要客户轮询就能向客户返回数据和事件的外部异步监视器来说,要
实现它也可以使用这种机制。
通过回调的异步执行










注意   上面各步的次序很重要,服务器返回客户的调用(第三步)是在它实际回调客
户(第四步)之前进行的。如果不按照这个次序而试图在第三步之前做第四步的话,部
件将无法完成回调请求,因为客户会在第二步开始的调用上阻塞。
 详细信息   关于回调的详细信息,请参阅“实现 ActiveX 部件”。

 七.指定部件的接口
部件接口定义了可以被其它部件访问的所有服务。它在本质上是一种约定。为部件指定
接口以后,就可以建立起一些条件,在这些条件下供应者部件就可以按照约定的形式向
消费者部件提供服务。部件接口定义中要指定的元素有:
■ 接口名称
■ 所支持的一些服务
■ 前提条件
■ 信箱条件
■ 相关性
■ 相互作用标准
 在分布式部件领域中,接口的具体设计是至关重要的问题。接口一旦实现和部署,那
么它在部件中就不能修改了。因此,定义接口时应该尽可能的灵活以及从长远考虑。
不要改写接口,这有助于避免新旧部件之间的冲突。如果需要向部件中增加功能或者要
对功能进行语义上的修改,那么应该为该部件创建一个新的接口,而不应该改变旧的
(已发布了的)接口。
详细信息   关于部件接口的详细信息,请参阅“部件工具指南”中“创建一个
ActiveX 部件”的“提供通知接口实现的多态”。

(一)部件的交互标准
为了从逻辑模型创建物理部件,最基本的是要把部件的交互标准化。在给定的应用程序
中,可以使用多种交互标准(比如存储过程和 ActiveX 部件);但是如果所有部件相
互之间的操作十分紧密,那么宁肯使用单一的标准。这方面的讨论将集中在使用
Visual Basic 提供的工具实现 ActiveX 部件。
交互标准可以与语言无关
部件可以使用很多类型的交互标准。但有些交互标准依赖于某一种语言或者实现,比较
好的选择是使用与语言无关的交互标准,比如 ActiveX 和分布式的 COM。
交互标准的其它例子,有远程过程调用 (RPC) 和传统的动态链接库 (dll),数据库触
发器以及存储过程。但是这些技术在建立部件方面,都有一定的局限性。
对于交互标准的评价,应该基于它是否适合于任务,以及是否符合企业的体系结构。在
考虑某个特定的标准时,要看它是否:
■ 允许部件放在远程并在远程活动。
■ 允许位置透明。
■ 清楚地执行描述了的服务接口。
■ 提供 LAN 协议的透明性(IPX、TCP/IP 等等)。
■ 具有运行时的协商能力。
■ 具有通过安全地引入新接口来支持扩展部件的机制。
■ 允许可替换的服务实现方式。
■ 有操作和管理连接的机制。

(二)部件的执行模型
本章的前面提供了关于部署的建造、实例化以及异步的各种选项,这里给出的是,在设
计分布式应用程序的部件行为时,可供考虑的三种基本执行模型。
部件的执行模型










1. 专用面向状态的对象
  在这种情况中,客户要创建服务器部件的一个单用途实例并在释放(破坏)之前要多
次访问这个实例。这种模型的优点是,允许服务器代表客户维护状态,这样就能够简化
客户必须维护的状态。(例如,在销售商完成一次客户订货的会话之前,订货对象需要
维护产品、数量和价格的清单,以及诸如此类的东西。)
 这种模型有时非常有效,尤其是在只读的或进行分析的方案中,但设计者必须保证能
够满足恢复的要求,因为运行服务器的机器(本地的或远程的)有可能出现故障,并导
致丢失用户的状态。在数据持久性要求高的场合,可以在属性状态改变之后采用数据库
全写。这种模型一般在本地的(单机)方案中使用,但它也可以用来维护多客户或多进
程的状态。(工作组的公告栏或讨论区,就是使用这种模型维护共享状态的一个例
子)。
  如果有很多用户都正在维护多个打开了的专用部件实例,那么从资源的角度看,这种
模型的代价会比较昂贵。如果这种模型是用在共享的机器配置上,同时又是按照专用方
式为请求分配客户的实例,那么设计者就要特别注意估计潜在负载的峰值。
2. 专用无状态服务
 在这种情况中,客户创建对服务器部件的引用(单用途或多用途),在因为其它的使
用而放弃该引用之前,客户对它只访问一次。这种模型的优点是,在事务方案中既强健
又有效,对于诸如 WAN 和 Internet 这种低速而脆弱的连接尤其是这样。强健是因为
对服务器的调用是原子的,并且在出现故障时能够很容易地重试,有效是因为服务器在
为客户执行服务时对于客户来说是专用的,只有在执行完服务以后,其它客户才能使用
它。
  这种模型同样需要客户向服务进程传递变量,作为函数(方法)调用的参数而不是作
为单独的属性设置的调用,这样就可以减少对服务器的反复调用了。
 虽然这种模型比面向状态的模型更有效率,但如果它是用在共享的机器配置中,那么
设计者仍应该仔细估计潜在负载的峰值。考虑到这种情况,可以使用缓冲池管理器(在
本节的后面讨论),既能改善部件的采集时间,也能管理运行的部件实例个数。还应该
注意的是,这个模型就是 Microsoft 公司的分布式事务处理技术将来要支持的模型。
  针对前面描述的订货处理的例子,有必要使用这个模型,让客户在用户桌面上创建面
向状态的订货对象,把所有的订货细节都放在这个对象中,把该对象传递给一个订货处
理服务。如果处理服务失败了,客户只要简单地向服务重新传递这个对象就可以了。
 注意   从效率出发,订货对象上应该单独有一个方法(比如 GetOrderDetails),使
服务由一个调用就能够获得全部订货细节。

3. 异步工人队列
  在这种模型中,客户向队列部件提交“作业”。队列部件向等待的作业列表中添加作
业,同时根据选项可保存对客户的引用(对象引用,电子邮件地址,等等),然后将控
制返回给客户。工人部件作为单用途对象运行,它调用队列部件来检查是否有需要处理
的作业。如果有,那么队列部件将向工人部件分配一个作业,并返回等待状态,准备接
收客户提交的下一个任务或者工人的新任务。如果一个作业完成了,那么可以由工作部
件或队列部件通知客户。
 使用这种模型的前提是任务的提交和作业的分配都非常快;这样队列部件就可以作为
单用途的服务器来实现,和与它连接的所有客户共享它的一个物理实例。另一方面,工
人部件可以实现为单用途部件,因为它们需要自己的执行线程来独立完成它们的任务。
队列部件经常要负责管理作业的优先级、创建和管理工人对象的实例,同时还可以报告
它所处理的任务的集中性能和帐单信息。如果合适的话,队列部件可以管理各种不同类
型的作业,但是对于提交给队列部件的各种作业类型,工人部件都必须能够处理。
  这种模型能做到非常有效和强健。有效,是因为客户的请求是面向批处理的,它们提
交得非常快,并且它们所使用的队列部件的动态资源也是最少的。(通常在作业序列中
只有一个新记录。)强健,是因为在大多数紧迫状态中,资源的分配可以完全保持在服
务器系统的一些最优级上。(不再请求式创建部件的实例和进程。)
 举例来说,队列通常每小时可以管理 100 个客户和 6000 次订货,但有时会突然收到
四倍于平时数量的订货高峰。如果运行队列的机器没有能力跟上这些高峰,那么唯一办
法就是减少内存数据量,用较少的内存来维护作业列表数组中每个作业。如果需要更进
一步的强健,甚至可以把作业列表放在一个事务型数据库中维护,这样即使服务器崩
溃,作业列表的完整性仍然可以得到保证。在共享的数据库上对队列的状态进行管理,
可以让其它机器上的伙伴队列服务器分担所有的处理负担,同时在一个服务器机器崩溃
事件中还可以让这些伙伴直接接管工作。
  队列模型不足的一面在于它所固有的特点,即面向批处理和异步。其操作一般要用
“足够的实时时间”而不是实时/同步的时间。这样一来,作业完成的时间可能就无法
精确地估计了,因为队列部件和它所在的主机上的负荷是随时波动的。在很多情况下这
不会成为问题,而有些情况下,则可能影响应用程序的全面设计和行为。
 举例来说,上面所说的订货处理方案,活动的订货任务已经完成,没有向操作者提供
同步收据。但在操作者桌面运行的用户界面上,对应地会有一个已提交的订货的列表,
表中每个作业的状态将在队列服务器完成该作业时被更新。用户界面部件维护提交作业
的列表,表中出现任何故障时这个界面都会自动进行重新提交。
  实现中还要强调一点,就是应该注意到多用途的实例模型,它本身能使队列部件防止
多个客户同时进入。这使得维护队列中管理作业状态的完整性变得十分简单。

八.“Booksale”应用程序中部件的部署给出了在“Booksale”示例应用程序中,所需
要的服务是怎样划分成独立的物理部件的。
下图中的部件对象,是按照本节的准则,从“Book Sale”示例应用程序中得出的。
“Book Sale”应用程序中的部件
  用户接口部件





   业务部件






   数据部件





注意,在该例的实际实现中,Jet 数据库引擎访问客户机上的本地数据库,而不是上面
显示的 BOOK_SVR 部件访问的远程客户/服务器数据库。之所以这么做,是因为本书的
读者并不一定都拥有专用的数据库服务器来测试这个应用程序。
在 BOOK_SVR 工程的函数声明中提供了一个部件,说明如何更有效调用远程数据库。

第四节 实现 ActiveX 部件
 实现 ActiveX 部件针对用于“远程自动化”和分布式 COM 的部件,介绍它们如何实
现。讨论了多线程、错误处理、异步回调、安全机制、可扩展性和系统管理。此外还介
绍了某些具体的实现,以及各种性能选项和优化的“经验和技巧”。
ActiveX 部件是提供业务、数据和应用程序服务的理想机制。用 ActiveX 部件实现某
种服务后,开发者就可以将这种服务用于遍及整个企业的应用程序。Microsoft Visual
Basic 5.0 版使创建 ActiveX 部件变得很容易。
用 Visual Basic 代码来实现 ActiveX 部件在《部件工具指南》的“创建 ActiveX 部
件”中有详尽的介绍。本章不再重复这些内容,而主要关注在企业环境中实现这些部件
时所伴随的独特问题,请参考“创建 ActiveX 部件”中的有关部分。
所谓 ActiveX 结构就是以前称之为 OLE 自动化技术的扩展。由于这种结构建立在部件
对象模式 (COM) 之上,因此 ActiveX 具备 Microsoft Windows NT 4.0 环境中分布式
COM 扩展版的优点,而且现在还可以在 Microsoft Windows 95 环境中使用。
与远程自动化类似,分布式 COM 为能够比较容易地在网络上扩展 ActiveX 与其客户端
应用程序之间的关系提供了基础。它允许应用程序在网络的任何地点访问 ActiveX 部
件提供的服务。
主题

 一.服务模型中的 ActiveX 部件ActiveX 部件在企业级应用程序的三层结构中起主要
作用。通过提供对服务进行封装的能力,以及提供将这些服务提供给应用程序和其它部
件的能力,ActiveX 部件可以为部署业务和数据服务提供完美的工具。
在服务模型中使用 ActiveX 部件的方法很多。ActiveX 部件可以部署在工作站上,不
管是作为进程内的服务器,还是作为进程外的服务器,都作为应用程序的部件安装在工
作站上。这样 ActiveX 部件就能够提供数据和业务服务,以及文件和磁盘管理实用工
具等一般的应用程序服务。
ActiveX 在远程网络服务器上运行的企业级应用程序中起到更大的作用。例如,就数据
服务而言,它们可以提供复杂查询的更高效执行;就业务服务而言,它们可对广泛使用
的业务规则提供一致的实现方案。
除了作为企业级应用程序中的部件提供服务之外,实现数据及业务服务的 ActiveX 部
件,也可以供使用了支持 ActiveX 的工具(诸如 Microsoft Excel、Microsoft
Project 和 Microsoft Access)的分析人员和预测人员使用。
“创建 ActiveX 部件”中深入讨论了本地 ActiveX 部件。其中很多内容对远程服务器
也同样适用,是下面讨论本地与远程 ActiveX 部件有关优点的基础,也是本节最后的
远程服务情况的基础。
数据服务
本地 ActiveX 部件提供的数据服务,包括本地数据库文件、网络数据库服务器、对传
统的数据的访问,以及所有这些服务的组合。本地 ActiveX 部件可以提供复杂的数据
服务,对来自若干源的信息进行组合和处理。
来自多个源的数据由工作站上的某个 ActiveX 部件收集、处理,然后返回它的客户应
用程序。大量的原始数据从这个集中的数据处理点传送到某个业务办公室的路途中,可
能要经过复杂的包含很多路由器和桥的通路。处理过的数据随后经过一段很短的通路,
从 ActiveX 部件到达同一机器上的应用程序用户接口部件。这是部署分布式 ActiveX
部件的许多实现方式中的一种。
另一方面,远程的 ActiveX 部件则可以放在接近公司的数据源的地点,从而缩短原始
数据的传送路途。企业网的服务器由于安全及操作的原因总是频繁地进行编组;远程的
ActiveX 部件可以利用这种天然的编组。
从远程服务器上多个源组织数据时,也能觉察到在性能方面的优点。运行远程 ActiveX
部件的计算机可以是比它所服务的工作站更强大的机器,并且在有些情况下,把数据库
和 ActiveX 部件放在同一台计算机上,可以进一步缩短数据通路。
当提供数据服务的 ActiveX 部件必须重复 SQL 请求来完成客户对数据的请求时,这一
技术就很有用。这是一个高深的内存技术,需要仔细管理计算机负载。可以使用缓冲池
管理技术来控制这样的状态(参阅本章稍后的“缓冲池管理器”)。
远程部件与存储过程
大多数 SQL 数据库都提供几种存储过程的格式。存储过程就是完全在数据库服务器上
执行的程序。依靠这些数据库,存储过程可以获取参数,创建中间结果集,返回多个结
果集,以及提供有限的程序逻辑。
将数据服务编译为 ActiveX 部件时,可以将存储过程的最佳点与程序性能和 Visual
Basic 的轻巧调试结合在一起。
存储过程具有很多优点,包括安全性、事务封装、以及参数确认。由于存储过程是由数
据库服务器执行的,因此可以直接访问数据。它们可以非常迅速地创建中间结果集、互
相联接、与其它表联结,产生一个小得多的最终结果。
在另一方面,由于存储过程驻留在数据库服务器中,调试很困难,而且它们的程序逻辑
通常颇受限制。很多若用 Basic 这样的过程性语言来写非常简单的任务,但要写成存
储过程必须使用一些希奇古怪的技巧。
用 Vsual Basic 编写 ActiveX 部件时有很多的第三方工具可以使用,如发消息,异步
通信,以及大型机连接。而大多数的数据库存储过程中,这些功能都是不可以使用或者
是很难使用的。
远程计算机上的 ActiveX 部件可以缩短数据通道,甚至可以和 SQL 服务器数据库常驻
在同一个远程计算机上。这种 ActiveX 部件可以检索中间结果,以及进行存储过程中
不能进行的计算,而且当使用存储过程更为有利时还可以使用存储过程。
业务服务
与数据服务类似,无论是本地的或者是远程的 ActiveX 部件都能提供业务服务。不管
是哪种情况,将服务包装为 ActiveX 部件有很多优点。
■ 各部门的专门知识可以在企业范围内共享。例如,财务部门可以提供标准的部件来
帮助分析其它部门的利润或者投资收益。
■ 为企业级应用程序开发的标准服务也可以被诸如 Microsoft Excel 和 Microsoft
Access 等最终用户工具调用,从而为业务分析和管理报告提供强有力的支持。
■ ActiveX 部件提供精心定义的接口,这些接口使得业务服务成为多个应用程序的部
件。
 将业务服务部署为多个工作站上的本地 ActiveX 部件可在工作站上提供更快的响应时
间,但它要求工作站上的部件保持最新版本。如果某个特定服务是频繁改变的,则选用
远程 ActiveX 部件可能更合适。
注意   如果本地 ActiveX 部件需要频繁更新,则可以使用如 Microsoft Systems
Management Services (SMS) 之类的工具来确保所有工作站上安装的是最新版本。在
《部件工具指南》的“创建 ActiveX 部件”中论述的 Visual Basic 的版本兼容特
性,简化了确保 ActiveX 部件向上兼容的工作。
 如果运用某条业务规则时需要数据服务,则可能从邻近那些服务的网络上得到性能上
的好处。当数据服务从多个源组合数据时,正常的企业操作过程必然会导致业务服务器
与数据服务相邻。
同一个 ActiveX 部件可以部署为本地使用也可以部署为远程使用,这样就给了应用程
序开发者很大的灵活性。部署在部门级开发的 ActiveX 部件,可进一步加工和定义,
使之适合一般情况,然后根据需要放到远程服务器上。如果有特定部门需要,也可以把
ActiveX 部件从远程服务器移至若干选定的工作站上。

 二.ActiveX 客户/服务器关系在这种服务模型中,客户/服务器关系的说法比客户/服
务器程序或者客户/服务器计算机的说法更恰当。“服务器”部件也可能是其它部件的
“客户”。这部分也介绍了 ActiveX 的标准是如何与分布式 COM 以及远程自动化一起
工作的。
在这种服务模型中,客户/服务器关系的说法,比客户/服务器程序或者客户端/服务器
计算机的说法更恰当。例如,应用程序的用户接口部件可以是提供数据服务的 ActiveX
部件的客户。而这个部件自己又可以是一个或多个远程数据库服务器的客户。
由于使用分布式 COM,提供数据服务的 ActiveX 部件可能在远程计算机上运行。该
ActiveX 部件也可能是客户,从其它本地或远程 ActiveX 部件或数据库服务器获取数
据或业务服务。
客户/服务器关系又可以是双向的。工作站可以将对象引用跨越网络传给在另一台计算
机上运行的 ActiveX 部件。当远程计算机使用该对象引用给工作站发送数据或通知
时,其客户/服务器关系就反过来了。这时远程计算机就变成了客户,而工作站成了服
务器。
(一)客户/服务器应用程序中的ActiveX部件
所谓 ActiveX 部件就是指根据 ActiveX 标准,可以将自己的对象提供给其它应用程序
使用的任何可执行代码。该 ActiveX 标准是基于部件对象模式 (COM) 的。
由于 ActiveX 与共享对象有关,ActiveX 部件就隐含了客户/服务器关系,在这种关系
中客户端请求对象,而服务器提供对象。不过,客户与服务器之间的区别不是很明显
的,因为同一个部件可以(而且经常是)既是客户端,又是服务器。由于这个原因,总
是称呼“ActiveX 部件”,而不是叫“ActiveX 客户”和“ActiveX 服务器”。不过,
在指定的执行范围中,客户/服务器关系是重要的。
客户/服务器关系两头的部件可以包含在同一个计算机上,也可以在由网络相连的不同
的计算机上运行。当 ActiveX 部件与其客户进程在同一个计算机上运行时,则称该
ActiveX 部件是本地的,否则该 ActiveX 部件就是远程的。
在独立的计算机上,ActiveX 部件是在进程内还是在进程外方式运行,是相对客户而言
的。进程内 ActiveX 部件是作为动态连接库(.dll 文件)来实现的,因此和它的对象
的使用者在同一进程内运行。进程外 ActiveX 部件则是在自己的地址空间运行的可执
行程序(.exe 文件),如 Microsoft Excel。
进程内 ActiveX 部件与其客户共享地址空间,因此对进程内 ActiveX 部件方法的调用
可以使用客户的堆栈。在该客户进程的地址空间内部通过引用传递参数时- 包括将参
数传给进程内服务器- 接受参数的过程可以直接访问该数据。
不过,其它进程中的方法不能使用该客户地址空间的指针。ActiveX 解决这个问题的办
法是将参数的数据复制到进程外部件的地址空间,然后用副本的指针替换原来数据的指
针。
ActiveX 部件方法使用这个指针来修改副本。当方法结束时,所有 ByRef 参数的数据
全都复制回客户的地址空间。按这种方式,跨越两个进程之间的边界来移动数据,被称
之为调度。
ActiveX 客户与进程外 ActiveX 部件通过由 COM 自动提供的代理/通讯模块机制进行
通信。代理和通讯模块负责处理传递给该部件方法参数的调度与散开;这对客户进程是
完全透明的。调度比在进程内传递参数要慢,特别是通过引用传递参数时。
不过,进程外 ActiveX 部件的服务可以被很多客户共享,虽然每个客户都必须有自己
的进程内服务器的副本。不仅如此,分布式 COM 能跨越网络自动扩展代理/通讯模块机
制,从而提供了企业级服务。
详细信息   请参阅《部件工具指南》中“创建 ActiveX 部件”的“ActiveX 部件标准
及指南”。
(二)分布式COM的工作方式
分布式 COM(有时称为 DCOM),Microsoft 的分布式对象系统,是指分布式部件网络
应用程序的 ActiveX 接口。DCOM 提供了网络透明及通信自动化,从而使一个对象无须
了解另一个对象的位置就能进行对象之间的通信。这些对象可以来自同一个机器上的不
同进程,或者来自不同机器上的独立进程。
分布式 COM 使用一种基于标准的远程过程调用,可以使在不同机器上运行的支持
ActiveX 的应用程序进行无缝互操作。这种 RPC 机制称为 Microsoft RPC,是基于开
放式软件基础的分布式计算环境的 RPC,并与之兼容。
分布式 COM 允许将应用程序划分为部件模块,每个部件模块可以在不同的计算机上透
明地执行。由于分布式 COM 提供了网络透明,这些部件对用户和编程人员而言就好象
装在一台机器上。
分布式对象技术也可以使全局的网络和信息资源看上去象是本地的,这就使用户更容易
也更快地访问重要的业务信息。通过分布式 COM 和远程自动化,用户可以在整个网络
内放置和执行部件,而根本无须知道该信息来自数千英里之遥的地方。
透明的跨进程互操作
如果可以假定所有部件之间的交互行为只发生在同一个进程空间,那么提供部件的软件
体系结构的问题就比较容易解决了。但是实际上通常不是这样。
本地与远程的透明性
ActiveX 标准被设计成允许客户透明地与部件通信,而无须关注部件在哪儿运行,是否
在同一个进程中,是在同一台机器上还是在不同的机器上。这就意味着对所有类型的
ActiveX 部件都是同样的编程模型。
从客户应用程序的观点来看,所有的 ActiveX 部件都是通过接口指针访问的。指针必
须是进程内的,而且实际上,任何对接口函数的调用总是先到达某些进程内的代码。如
果该 ActiveX 部件是进程内的(.dll 文件),就可以直接调用。如果该部件是进程外
的,那么调用首先到达一个由 COM 本身所提供的称为代理的对象,然后 proxy 对象产
生相应的对其它进程或其它机器的远程过程调用。
从 ActiveX 部件的观点来看,所有对部件接口函数的调用都是通过该接口的指针发出
的。另一方面,指针只在单一进程内有效,因此调用者必须是进程内的代码段。如果
ActiveX 部件是进程内的,调用者就是客户自己。否则,调用者就是通讯模块对象-
由 COM 提供的- 该对象接收来自客户进程代理的远程过程调用,并将其转为对该服务
器部件的接口调用。
就客户和服务器双方而言,它们总是直接与某些其它的进程内代码通信。
一旦部件的地址在系统的注册表注册之后,基本结构对于本地或远程 ActiveX 部件的
处理是一致的。本地/远程透明性有很多重要的优点:
■ 问题的公共解决方案,它与客户和服务器的距离无关。例如,连接、函数调用、接
口协商、功能演化等等,对于在同一进程中相互操作的部件与跨网络相互操作的部件,
都是严格按同一种方式产生的。
■ 编程人员很容易学会。新的服务只简单地通过新的接口来展现,一旦编程人员学会
了如何处理接口,就已经知道了如何使用未来新创建的服务。
■ 部件设计者可以集中精力进行设计。在为企业应用程序设计部件时,设计者可以集
中精力于总体设计,而不必为各种互操作情形考虑低层的通信机制。分布式 COM 无偿
提供了这些机制,包括网络透明性。

(三)远程自动化的工作方式
远程自动化是在 Visual Basic 4.0 版的时间框架中介绍的- 在介绍分布式 COM 之
前。它提供很多同样的功能,但效率不高,也不是自包含的。Visual Basic 5.0 版的
企业版继续支持远程自动化,包括向后兼容,以及对客户端进程必须位于 16 位机器上
的情形的兼容(分布式 COM 不支持 16 位的体系结构)。
远程自动化是这样来实现的:用可以使用远程过程调用 (RPC) 协议在网络间通信的模
块,来替换原来的代理和通讯模块。远程自动化的代理包含在 Autprxxx.dll(这里的
xx 为 16 或 32)中。在远程计算机上运行的通讯模块,则是 Visual Basic 随带的自
动化管理器中的一部分。
远程自动化的通讯模块负责发散 RPC 调用。自动化管理器使用标准的 COM 代理来调度
传送来的信息,将其传递给运行在远程部件的进程中的通讯模块,由通讯模块将这些信
息发散出去。返回值则由 COM 通讯模块调度,并传递给自动化管理器,为了将返回值
送回客户计算机,由远程自动化的代理负责发散,返回给 ActiveX 客户。
客户计算机上的 Windows 注册表的设置值决定 COM 是使用其标准代理,还是使用远程
自动化的代理。可以使用远程自动化连接管理器实用工具来改动该设置值,这在“客户
/服务器工具”介绍。
重点   尽管代理和通讯模块是由 COM(以及分布式 COM)处理的,自动化管理器必须
在工作站上的 ActiveX 客户用远程自动化请求远程服务之前就在服务器上运行。

 三.远程部件的设计问题对建立 ActiveX 部件的实现方案和性能问题的简要总结,请
参考“创建 ActiveX 部件”中的有关主题。
由于分布式 COM 和远程自动化在部件级是透明的,那么设计问题对本地和远程
ActiveX 部件都是一样的。。)这些问题在《部件工具指南》中的“创建 ActiveX 部
件”一节中有详尽的论述。以下部分论述这些问题。
多线程,阻塞与单用途服务器
Visual Basic 5.0 版的一个最令人兴奋的新功能就是支持创建多线程的、使用房间模
型的线程安全部件。该功能对于没有可视用户界面元素的部件是可用的。如果在编译时
将该部件标记为“无用户界面执行”,则该部件就有了该功能。
多线程的 ActiveX 部件,以及与这种部件的使用相关的各种问题和机会,在《部件工
具指南》的“创建 ActiveX 部件”的“编译代码部件”中都有详尽的介绍。
异步通知
 在 Visual Basic 中用 ActiveX 部件来实现异步通知有两种独立的机制:回调对象与
事件陷落。这两种方法均在《部件工具指南》的“创建 ActiveX 部件”的“异步回调
与事件”中介绍。
实现远程部件中的标准接口
由于公布了接口标准,部件互操作的 ActiveX 模型就能允许应用程序之间自由交换对
象及其属性、方法和事件,而不管该部件是用什么语言编写的。
ActiveX 接口标准在《部件工具指南》中“创建 ActiveX 部件”的“部件设计的一般
准则”和“ActiveX 部件标准与准则”中介绍。
可扩展性的设计
可扩展性是指部件在不同的范围处理不同的工作负载的能力。在适度使用时表现良好的
部件,在移动到一个更繁忙或用户更多的网络中时可能就不堪重负了。
实现可扩展性的途径在《部件工具指南》“创建 ActiveX 部件”的“编译代码部件”
的“可扩展性与多线程”中介绍。
一般的性能问题
任何 ActiveX 部件,如果其对象正被另一台计算机上的客户使用,则会受到很多进程
外 ActiveX 部件的性能问题的影响。
对来自进程内的服务器,并作为另一个服务器的客户运行的对象来说,也是如此。当对
其对象的引用被传递到另一台计算机时,它们的方法至少要被跨进程调度一次,这是由
于自动化管理器的缘故。
这些问题包括:
■ 限制可创建对象的数量。
■ 传递参数与设置属性的比较。
■ 调度与数据包的大小。
 关于 ActiveX 部件性能问题的更详细信息,请参阅《部件工具指南》的“创建
ActiveX 部件”的“ActiveX 部件标准及指南”。

 四.远程自动化与分布式 COM 的安全性安全性对于远程对象的创建是很重要的,因为
分布式 COM 和远程自动化允许执行远程机器上的代码。若没有足够的安全保证,这就
意味着对安全的严重威胁。
分布式 COM 的缺省安全策略可以防止远程计算机不经授权地创建对象。只有那些经远
程计算机的系统管理员授权的对象才可以远程地创建。
由于分布式 COM 和远程自动化十分精简,所以创建远程对象的安全性很重要。决定客
户是使用本地 ActiveX 部件,还是使用运行在远程计算机上的同样的部件的唯一依
据,就是该部件在这个客户计算机的 Windows 中的注册项。
由于提供的安全保护比分布式 COM 更少,所以这对远程自动化十分必要。
例如,假设已经在远程计算机上安装了可编程的最终用户工具。自动化管理器也在这个
远程计算机上运行,但其安全策略已被设置为“允许所有远程创建”。如果某个用户的
工作站上有这个被安装的可编程工具的副本,则该用户就可以改变其工作站的注册项,
使它们指向该工具在远程计算机上的副本。
如果远程自动化代理安装在这个工作站上,或者远程用户可以获得一份副本,那么他们
现在可用任意的自动化客户应用程序,来启动和控制这个可编程工具的远程副本。这也
包括从网络共享加载和运行用户编写的代码。充分利用自动化管理器提供的安全保障能
力是很重要的。设置为“允许所有远程创建”的安全策略,只能由开发者用在用来做开
发的机器上。
设置安全策略
分布式 COM 和远程自动化为运行远程 ActiveX 部件的 32 位网络计算机提供了四种安
全策略设置。可以使用远程自动化连接管理器为网络计算机设置访问控制级别,这在本
节以及“客户/服务器工具”中描述。
如果使用安装程序向导 (SetupWizard) 来为将在远程计算机上运行的 ActiveX 部件创
建安装程序,则连接管理器会自动包括在安装程序中。为了设置安全策略,连接管理器
必须在远程计算机上安装和运行。
连接管理器的“客户访问”选项卡允许为远程计算机选择以下的系统安全策略设置值中
的一种。其中的“值”对应的是 RemoteActivationPolicy 设置的优先次序。
名称       值  描述

“禁止所有远程创建”   0  不允许创建任何对象。
“允许通过关键字进行远程创建”   2  只有当“允许远程激活”复选框被选中时,才
能创建对象。
这使得Windows 注册表中的 CLSID
改为包含下面的子键设置:AllowRemoteActivation=Y
“允许通过 ACL 进行远程创建”   3 只有 Windows 注册表中的 CLSID 的访问控制列
表包含该用户时,该用户才能创建对象。只对 Windows NT 适用。
“允许远程创建”    1  可以创建任何对象。建议只在开发环境中使用。

关于这些访问级别的详细说明见下面各小节。
注意   公用类的类标识符 (CLSID)- 以及这个类所创建的对象的类标识符- 是一个
128 位的整数,在任何计算机的 Windows 注册表中唯一地标识该类。在 Visual Basic
中要使用该类的对象时,无须知道其类标识符的值。
 当使用 Visual Basic 创建 ActiveX 部件时,每个类模块的 CLSID 都是自动创建
的,并存放在该 ActiveX 部件可执行程序的类型库中。当 ActiveX 部件注册时,它的
CLSID 就被添加到 Windows 注册表中。
“禁止所有远程创建”
使用该设置,只要客户计算机试图在该远程计算机上创建对象,就会返回 错误
&H80070005— “访问被拒绝”。在 Visual Basic 中,这将导致第 70 号错误“权限
不够”。
“禁止所有远程创建”的设置不会妨碍运行自动化管理器的网络计算机将对象引用传递
给其他计算机。这对于保护工作站和辅助的网络计算机很有用处。
工作站一般都有 Microsoft Excel、Microsoft Access、或其他可以使用自动化控件的
工具。在工作站上指定“禁止所有远程创建”会阻止其它用户远程控制这些应用程序,
尽管仍允许将对象引用传递给远程 ActiveX 部件。
“允许通过关键字进行远程创建”
这是远程自动化安全策略的缺省设置。当自动化管理器接收到创建类的对象的请求时,
先检查 Windows 注册表中的子键及设置,看是否允许其它计算机上的客户创建该对
象。
按照缺省规定是不能创建对象的。如果请求的类没有使用子键标记,则不能创建该类的
对象。错误 &H80070005— “访问被拒绝”就被返回给发出请求的客户。
安装了 ActiveX 部件的远程计算机的系统管理员,使用“连接管理器”中的“客户访
问”选项卡,就可以控制该 ActiveX 部件的哪个类可以被创建。在安装 ActiveX 部件
之后,系统管理员在连接管理器的 OLE 类列表中选择必要的类,然后选择“允许远程
激活”选项。ActiveX 部件的安装指示应告诉系统管理员哪些类需要标记。
没有必要指示系统管理员去标记所有 Public 属性为 True 的类。ActiveX 部件本身就
阻止客户创建任何 Instancing 属性被设置为 Not Createable 的类的对象。
注意   如果允许远程激活属于支持自动化的可编程应用程序(如 Microsoft Access
或 Microsoft Excel)的类的话,远程用户就可以激活这些应用程序并在远程计算机上
运行它们的代码。这样做不利于远程计算机的系统管理。
“允许通过 ACL 进行远程创建”
设置值“禁止所有远程创建”、“允许所有远程创建”和“允许通过关键字进行远程创
建”对所有用户都适用,没有什么区别。设置值“允许通过 ACL 进行远程创建”只有
在运行 Windows NT 的网络计算机上才可使用,该设置值可以明确指定哪些用户可以创
建某类的对象。
如果“允许通过 ACL 进行远程创建”有效,任何创建对象的请求都会导致自动化管理
器暂时以该客户计算机用户的身份去试图打开注册表中的 CLSID 键。只有该用户具有
读取该键的权限时才能创建该对象。
 给指定用户或用户组授予创建对象的权限
在安装该 ActiveX 部件的网络计算机上,启动“远程自动化连接管理器”
(Racmgr32.exe)。
1. 单击“客户访问”选项卡。
2. 在“COM 类”列表中,选择允许创建其对象的类。
3. 单击“编辑 ACL”按钮打开“远程类权限”对话框。
 如果当前的安全策略不是“允许通过 ACL 进行远程创建”,该按钮将被禁止。
4. 在“访问类型”框中,选择“特定访问”打开“特定访问”对话框。
5. 在“其他”框架中选择“查询值”。这将选择框架标题旁的选项按钮。单击“确
定”。
6. 在“远程类权限”对话框中单击“确定”。
 注意   如果远程计算机上的 ActiveX 部件,给在 Windows 的 16 位版本上运行的客
户提供对象,使用“允许通过 ACL 进行远程创建”将导致 16 位客户产生问题。16 位
进程第一次调用远程计算机时,该客户机器的用户不得不通过响应“输入域口令”
(Enter Domain Credentials) 对话框来确认网络登录。
“允许所有远程创建”
使用该设置,自动化管理器允许任何客户在网络的任何地点,创建任何公共类的对象,
只要这个公共类在该服务器计算机的 Windows 注册表中注册过。
该设置对正在其上进行开发的服务器来说是很有用的。在使用部署的应用程序时不推荐
这种设置。
访问控制的限制
远程自动化的访问控制只用于创建新对象。一旦客户部件已在远程 ActiveX 部件上创
建了对象,在理论上,即使该计算机没有创建该对象的权限,它也能够将该对象的引用
传递给另一个在第三台计算机上运行的客户部件。
用这种方式传递对象引用是在应用程序的开发者的控制之下的。应用程序的开发者应该
不让受控制的程序得到对象引用,开发者应该提防这个问题。
RPC 和权限传递的局限
在当前的 Windows NT 版本中,安全的权限传递并不连锁。安全的权限传递是指如果用
户 A 正在运行客户部件,该客户部件又访问远程计算机上的 ActiveX 部件 B,则用户
A 的权限就用来确定是否可以使用服务器 B。
假设服务器 B 然后又访问另一个网络计算机上的远程 ActiveX 部件 C。如果安全的权
限传递是连锁的,则用户 A 的权限也要用来确定是否可以访问 ActiveX 部件 C。
不过,由于安全的权限传递没有连锁,所以只有服务器 B 有权限确定是否可以在服务
器 C 上创建对象。
详细信息   关于建立 ActiveX 部件的更详细信息,请参阅《部件工具指南》中的“创
建 ActiveX 部件”。

(一)数据确认
远程过程调用 (RPC) 确认是指跨网络的两个计算机之间的通信时保证数据完整的级
别。RPC 为在任何 Windows 操作系统上运行的远程 ActiveX 部件提供七种确认等级,
如下表所示。
值 名称      描述

0 Default  使用网络的缺省值
1 None  不确认
2 Connect  到服务器的连接被确认
3    Call  当服务器接收到请求时,只在每个远程过程调用的开始进行确认。对基于
连接的协议序列(以前缀 "ncacn" 开始)不适用。
4 Packet  验证所收到的所有数据是来自期望的客户。
5 Packet Integrity 验证在客户端与服务器之间传送的数据没有被改变。
6 Packet Privacy 验证上述所有级别,并且对每个远程过程调用的参数值加密。

这些级别按递增的确认顺序排列。每个新级别添加在前一级别提供的确认上。如果 RPC
的运行时库不支持指定的级别,它会自动升到下一个更高的受支持的级别。
进一步的信息,包括用于 C/C++ 程序的 RPC 常数名,都可以在 RPC 的联机帮助中通
过查找确认级别常数得到。
确认的使用
选择 RPC 的确认级别时要仔细考虑,因为随着 RPC 确认级别的增加,性能相应下降。
可以为 ActiveX 部件中的每个类指定一个确认级别,从而不需要在整个服务器范围内
都使用如加密这种成本很高的确认级别。
例如,一个作为远程 ActiveX 部件实现的数据服务可能有一个 Logon 类用来传送用户
和口令信息,那么这个类就可能要用 Packet Privacy 确认级。而服务器公开的其它类
则可以使用低得多的确认级。
确认级别在客户计算机的 Windows 注册表中指定,位于远程对象的 CLSID 之下。对应
的子键名字为 'AuthenticationLevel'。如果没有该子键,则用 None。如果该值不是
上面所列出的值之一,则会产生 RPC 运行时错误。
可以为使用远程服务器的客户选择缺省的确认级别,对需要更严格确认的特定类可以重
新设定确认级别。


第三章数据访问选项

对数据的访问是客户/服务器应用程序的中心环节,Microsoft Visual Basic 企业版提
供了一套异常丰富的选项,用于在基于部件的应用程序中实现数据访问。
这些选项包括数据访问对象 (DAO)、远程数据对象 (RDO)、远程数据控件 (RDC)、开放
式数据库互连 (ODBC) 应用程序编程接口和 Microsoft SQL 服务器的 VBSQL 接口。
5.0 版介绍了大量新增加的数据访问特性,其中包括对 DAO 和 RDO 模型的重要扩充。
此外,新增加的事件驱动的编程范例允许异步操作,这要通过使用事件和 Visual
Basic 管理执行多线程的能力来实现。这种增强使应用程序不需要轮询异步事件是否完
成,而且可以使用并发线程使应用程序更加高效和快速。
第一节 理解远程数据访问选项

 理解远程数据访问选项 介绍了在企业版中访问远程服务器上数据的各种编程模型,
并且介绍了选择数据访问策略的主要的系统和设计问题。
Visual Basic 提供多种访问远程客户/服务器数据库的方法。这其中,有的技术使用
Data 控件或数据访问对象 (DAO),或者使用 RemoteData 控件与远程数据对象
 (RDO),有的技术则使用 VBSQL 或 ODBC API 之类的数据访问应用程序接口 (API)。
同样,在这个版本中,Visual Basic 允许单独使用 DAO或与 Microsoft Jet 数据库引
擎结合使用 DAO。下面的图表对这些选项作了说明:















对于特殊的数据库管理系统 (DBMS),最好的程序模型所依赖的若干因素,将在本章中
讨论。
主题
 一.新的客户/服务器数据访问选项概述概述了此版本 Visual Basic 的新功能,包括
相关主题的详细信息。
Visual Basic for version 5.0 中加入了许多新的数据访问功能,包括 DAO 和 RDO
对象模型的扩展。另外,它添加了一个全新的数据访问范例──事件驱动的程序设计。
开发者首次可以执行那些通过事件指示状态和完成情况的异步操作。应用程序再也不用
轮询测试异步事件的完成情况,而且在许多情况下,能够激活另一个进程的线程,从而
更有效率、反应也更加灵敏。
利用 DAO 中新的 ODBCDirect 功能,代码无需使用 Jet 引擎就可以利用现有的 DAO
代码使用远程数据对象。如果应用程序设计时未使用 DAO 索引顺序访问方法 (ISAM)
的对象和方法,则 ODBCDirect 意味着更精小、更快捷的远程数据资源访问,而且这种
访问无需从根本上重新设计现有的应用程序。
下面的主题介绍选出的一些新功能:
★ 新的客户/服务器数据访问对象 (DAO) 功能   介绍 DAO 的新功能,包括对 RDO 的
支持、DAO/Jet 体系结构的分割以及增强的错误管理。
Visual Basic Version 5.0 包括了 Version 3.5 的 Jet 数据库引擎。虽然 16 位的
Jet Version 2.5本不再包括在 Visual Basic 之中,但是 Jet 3.5 引擎可以读写 Jet
1.0 - 2.5 ( 16 位) 数据库。Visual Basic Version 5.0 也可以创建或修改 Jet 3.x
Version   的数据库结构。一般说来,Jet/DAO Version 3.5 支持现有的 Jet 3.0
Version 的功能;DAO 3.5 的其它新功能包括:
■ 对远程数据对象的支持。通过扩展 DAO 对象模型使其包括 DBEngine 对象的
DefaultType 属性及一个新的 Connection 对象,DAO 现在支持 ODBCDirect 模式,这
种模式无须使用 Jet 数据库引擎。在 ODBCDirect 启用后,DAO 使用远程数据对象
(RDO) 完成它的功能。
■ 对 DAO/Jet 体系结构分割,消除某些对象不必要的加载。将那些最经常使用的对象
──其中包括 DBEngine、Workspace、Connection、Recordset、QueryDef、Parameter
以及 Field 对象,与其它相对来说不是最经常使用的对象分开管理。
■ 增强的错误管理。现在 DAO 阻止使用对象不支持的功能。例如,试图使用
ODBCDirect object 不支持的 Jet DAO 功能会触发一个可捕获的错误。
 现在,Visual Basic Version 5.0 的 Data 控件包括若干新属性,它们的作用是启动
和使用 ODBCDirect 与 RDO 的接口:
■ DefaultType 属性。设置或返回工作区的类型 (Jet 或 ODBCDirect)。
■ DefaultCursorType 属性。设置或返回游标的类型 (ODBC、客户批、服务器端或
“非游标的”)。
 注意   Data 控件并非对 RDO 2.0 的所有附加的事件,方法和属性都支持。它仅支持
DAO 3.5 ODBCDirect 功能。
详细信息   “使用远程数据库数据访问对象”提供了关于使用 DAO 进行远程数据访问
的详细信息。

★ 新的远程数据对象 (RDO) 功能   总结 RDO 的新内容,包括事件驱动的程序设计、
新的客户批游标库、开放式批查询等等。
经过扩展和调整,远程数据对象 (RDO) 接口能够提供更好的综合性能并能处理许多实
际开发问题。RDO Version 2.0 总体上兼容 RDO 1.0 代码。然而,
rdoPreparedStatement 对象已经过时。虽然 RDO 2.0 仍然支持
rdoPreparedStatement 对象,但是它应该由新的 rdoQuery 对象所取代,后者支持
rdoPreparedStatement 对象的超集功能。下面是 RDO Version 2.0 其它新功能的概述

■ 新的客户批游标库。通过合并基于 Rushmore 技术的游标引擎,(为 Visual
FoxPro 开发),Microsoft Visual Basic Version 5.0 不仅提供大多数高性能的客户
端游标操作,而且允许提交在单一操作中执行的大块操作查询。虽然仍可以使用 ODBC
游标库,但是除此之外,现在还可以选择客户端的游标库。
■ 开放式批查询。RDO 2.0 不仅增强了新的客户批游标库处理开放式批更新的能力,
而且提高了基于事务处理的系统的性能,并减少了客户与服务器之间的网络通信量。批
操作通过一系列相关的操作查询影响若干行。开放式批工作的前提是:其它操作不大可
能与此操作竞争。换句话说,其他人不大可能在该操作取数据和更新数据期间变更数
据。
■ 事件驱动的程序设计。RDO 2.0 同样将事件集成到其程序设计模型的几乎每一方
面。这意味着可以打开一个与远程服务器的连接或创建一个复杂的游标,然后等待其结
束或超时的时候激发事件过程。这种新模式为异步操作开创了新天地。因为代码可依赖
独立的事件提供异步操作的完成信号,而不用一边等待完成一边轮询了。
■ RDO 对游标更新行为的控制。实现 Visual Basic Version 4.0 中的游标,要求直
接引用数据库表的 SQL 语句建造可更新的游标。这种方法不恰当地将应用程序与数据
库结构描述结合起来,费力而不灵活。为了分离结构描述,增强业务规则,IT 开发者
使用存储过程执行数据库修改操作。RDO 2.0 支持这种体系结构,它在数据修改周期的
若干关键时刻调用事件,从而允许存储过程执行时完成的实际变更。这样,可以使用一
个存储过程创建游标并使用另一个存储过程修改选定的行。这种技术也可用于
RemoteData 控件。
■ 自持的 RDO 对象。在某些情况下,RDO 2.0 支持“自持的”对象概念。除了使用
Open 方法,还可以通过“Dim <变量> as New <对象类型>”语法将 rdoConnection 与
rdoQuery 对象独立地实例化。一旦实例化后,就可以把这些对象添加到它们的关联集
合中,或者把它们同其它对象关联。现在,允许开发者把从 RDO 对象中派生出的对象
插入到 RDO 集合,这样就可以扩展 RDO,为特殊的 DBMS 及业务用途创建自定义控
件。
  例如,可以调整 rdoQuery 对象的大小,设置 SQL 属性及其它,通过设置
ActiveConnection 属性使其与 rdoConnection 对象关联,然后执行。这时,可以变更
ActiveConnection 属性,使其引用一个不同的连接,这样开发者无须创建另一个对象
就能在另一个连接上重新执行查询。另外, rdoResultset 对象可在代码中由
OpenResultset 方法创建,或者可从它当前的连接中分离出来,这样,它就成为数据的
静态快照。可以通过使用这种机制及状态标志,实现服务器之间的数据传送。
■ 类似方法的查询。RDO 2.0 支持创建引用存储过程的 rdoQuery 对象,所引用的存
储过程可以有参数、输出参量、返回状态值,也可以没有。在 RDO 2.0 中,存储过程
和其它查询就象方法一样易于调用,因为它们在其父 rdoConnection 对象上以新方法
的形式出现。
详细信息   “使用远程数据对象和 RemoteData 控件”是使用 RDO 访问远程数据的更
全面的指南。

★ 新的远程数据控制功能   提供了一个表,此表概述了六种实现开放式客户批查询更
新操作的新属性。
Visual Basic Version 5.0 RemoteData 控件现在具有六种用来实现开放式客户批更新
操作的新属性。所有这些属性都是现有 RDO 2.0 属性的实现:
属性     描述
DefaultCursorType   设置或返回游标库:ODBC、服务器端、客户批或“非游标的”。
BatchCollisionCount  返回一个数值,指定 BatchCollisionRows 属性(在最近的批
更新操作中,有多少行更新失败)中项目的个数。
BatchCollisionRows   返回一个书签数组,它指示在上一次的批更新操作中产生冲突
的行。
BatchSize    返回或设置一个数值,指定每个批中送回服务器的语句数目。
UpdateCriteria  返回或设置一个数值,指定在每个开放式批更新操作中如何为每一行
构造 WHERE 子句。
UpdateOperation  返回或设置一个数值,指定开放式批更新中使用 UPDATE 语句还是
使用后跟 INSERT 语句的 DELETE 语句。
详细信息   “使用远程数据对象和 RemoteData 控件”是使用 RDO 访问远程数据的更
全面的指南。

 二.客户/服务器数据访问程序模型的选择概述开发模型的功能,效益和局限。包括各
个模型相关主题的详细信息。
如“实现 ActiveX 部件”所描述,数据库服务器管理向客户/服务器应用程序提供服务
的部件的数据。通过 Data 控件、RemoteData 控件、基于 API 的程序设计接口及一个
或多个驱动程序,可以实现 Visual Basic 创建的数据库应用程序与服务器之间的通
讯。在选择应用程序设计模型之前,理解开发模型的功能、效益和局限是十分重要的。
选择灵活的数据访问程序设计模型
利用 DAO 或 RDO 程序设计模型,应用程序不仅更易于编写,而且与多种 ISAM 或远程
引擎的数据库完全兼容。在目标数据库引擎经过一段时间后可能变更或可能升级的情况
下,DAO 或 RDO 程序设计模型可以显著地减少适应新服务器数据库的时间。
ODBC API 程序设计方法的实现似乎有些困难,但它同样提供许多与 DAO 和 RDO 模型
相同的可移植性。虽然使用这种方法时不写入任何数据对象,但是 ODBC API 支持对任
何具有 ODBC 驱动程序的后端数据库的访问。通过向驱动程序查询服务器专有功能,可
以自动适应远程数据库的功能。
数据访问的开发更加容易
四种程序设计模型最显著的不同在于它们编写代码和利用数据识别绑定控件的方法。只
有在 DAO 设计模型中使用的 Data 控件与 RDO 设计模型中使用的 RemoteData 控件支
持绑定控件的使用,这种控件的使用大大减少了需要开发、测试、支持的代码数量和复
杂程度。虽然 ODBC API 是一种用于创建与数据库独立代码的统一接口,但是它没有对
象模型,也不支持数据识别型绑定控件。VBSQL 方法仅仅支持对 Microsoft SQL 服务
器的访问,它不支持对数据识别绑定控件的访问。
运用 DAO 或 RDO 程序设计模型,特别是当使用了 Visual Basic 或第三方控件时,将
显著地减少开发和测试应用程序所花费的时间。同时,也节省了开发者的培训时间。
下面的主题比较了这些模型,这将帮助您作出决定。
★ 使用数据访问对象 (DAO) 创建客户/服务器应用程序      概述了 Data 控件、绑
定控件和数据访问对象 (DAO),可以通过 Microsoft Jet 数据库引擎实现它们,也可
以不通过 Microsoft Jet 数据库引擎实现它们。
大多数 Visual Basic 数据访问的文档讨论 Data 控件、DAO 以及 Microsoft Jet 数
据库引擎如何与客户端数据库交互作用。此主题概述如何使用 DAO/Jet 访问远程 ODBC
数据库──特别是与其它程序设计模型的关系。关于 Jet 与 DAO 的更深入信息,请参
阅 Jet Database Engine Programmer's Guide。
Visual Basic Version 5.0 仅包括 32 位的 Jet 3.5 数据库引擎──没有提供 16 位
的 Jet 2.5 引擎。Jet 3.5 引擎能够读取任何以前的 Jet 数据库,也可通过 DAO 方
法创建 16 位数据库。同样,Version 3.0 版之前的 Jet 无法访问 Jet 3.x 数据库。
在转换 Jet 数据库之前,即使这个数据库只是通过附加表提供与外部数据库的链接,
也要确认现有的应用程序是否能够访问新版的数据库。
注意   访问 Jet 数据库的唯一方法是通过 Jet 引擎。即使使用 ODBC 访问 Jet 数据
库,ODBC 驱动程序也要加载适当版本的 Jet 数据库以实现与数据库文件的连接。
在 ODBCDirect 与 Jet-ODBC 之间作出抉择
Visual Basic 5.0 Version   添加了利用现有 DAO 代码访问远程数据资源的另一种方
法──ODBCDirect。这种模式不调用 Jet 数据库引擎,而是使用大多数相同的 DAO 对
象、方法和属性访问 RDO 接口。因为没有加载 Jet,应用程序内存遗迹节省了至少 1
MB。ODBCDirect 提供了与 RDO 库的 DAO 接口。换句话说,当代码打开 DAO
Recordset 对象时,ODBCDirect 接口将创建一个基本的 RDO Resultset 对象来获取数
据。因此,应用程序必须与 RDO 应用程序的设计要求一致。例如,它必须使用 32 位
版本的 Visual Basic,而且必须运行在 32 位操作系统之上。关于其它设计考虑,请
参阅本章中“使用远程数据对象 (RDO) 创建客户/服务器应用程序”一节。
使用 ODBCDirect 代替 DAO/Jet,可以体会到数据访问性能的显著提高。然而,在使用
ODBCDirect(或 RDO 本身)时,将无法使用某些 Jet 功能。例如,某些类型的
Recordset 对象、使用高速缓存的连接管理、异种连接以及 Jet 数据库对象访问都被
禁止了。另外,在使用 ODBCDirect 时,并非允许使用所有的 RDO 2.0 功能。例如,
DAO 对象的 RDO 事件在此时无法触发。
因为每一个 DAO Workspace 对象都可启动或禁止 ODBCDirect ,所以可以同时使用
DAO 与 ODBCDirect。可以使用 CreateWorkspace 方法同时创建 DAO/Jet 和
ODBCDirect Workspace 对象。
注意    对于 ODBCDirect 创建的游标与其它对象,Jet/DAO 对象或 RDO 无法识别,
反之亦然。这就是说,在 ODBCDirect Workspace 对象中创建的 Connection 对象无法
创建基于 Jet 的 Recordset 对象。
激活ODBCDirect
在 Workspace 对象创建之前,设置 DBEngine 对象的 DefaultType 属性指定 DAO 访
问远程数据库的方法──是使用 Jet 还是使用远程数据对象。也可以通过将
DefaultType 类型指定为 dbUseODBC 激活 ODBCDirect。利用dbUseODBC,DAO 不加载
Jet,而是使用 RDO 接口访问远程数据资源,图 9.1.描述了这一点。
如果指定 DefaultType 属性为 dbUseJet(或不作任何选择),则所有由 Jet 数据库
引擎实现的 DAO 功能都被启用。如果应用程序使用 ISAM 数据库或需要Jet 提供的附
加服务,则必须使用 dbUseJet 选项。
如果应用程序仅仅访问 ODBC 数据库,则 ODBCDirect 是程序设计模型中较好的选择,
具有高性能与较小的内存占用。如果设置 DefaultType 属性为 dbUseODBC,Jet 数据
库引擎将不被调用,DAO 通过 RDO 接口传递所有的数据访问请求,而后,RDO 接口使
用 ODBC 驱动程序管理器,通过其特定的 ODBC 驱动程序与远程的服务器通信。
图 9.1   使用 ODBCDirect 访问远程数据












通过 Jet 访问远程 ODBC 数据库
缺省状态下,通过 ODBC 驱动程序管理器及特定的服务器驱动程序,DAO 使用 Jet 数
据库引擎与远程数据库通信。(如图 9.2 所示)。 这种方法将开发者与程序设计中繁
琐的部分隔绝起来,同时提供了高层次的数据访问对象、属性、方法及复杂的连接管理
方案。使用 DAO 对象、方法与属性,应用程序无须或仅须稍稍变更以适应远程数据库
的不同,就可以访问多种远程和本地数据库。Jet 还支持异种连接── 其它接口无法
提供的功能。这意味着可以附加或链接到几个不同的数据库(不仅可以有本地的,也可
以有远程的)并执行 SQL 联结或其它查询── Jet 数据库引擎处理所有的细节。
图 9.2   使用 DAO 和 Jet 数据库引擎访问远程数据











 当使用 DAO 或者使用 Data 控件及与其关联的绑定控件时,除非激活 ODBCDirect 选
项,否则将自动调用 Jet 引擎。可以通过 DAO 编程和操作 Data 控件控制 Jet 引
擎。虽然一起使用 Data 控件和绑定控件可以显著地缩短开发时间,但是不明智的使用
将降低性能并增加加载时间。除非十分小心,否则 Data 控件将给可利用的数据库连接
数目及网络本身带来显著的负担。
提示   除非在设计时禁止 Data 控件并只在需要时启动,否则它至少将消耗一个远程
数据库连接。禁止 Data 控件,只需设置 RecordSource 或 DatabaseName 属性为空。
在运行时启动 Data 控件,只需填充置空的属性并使用 Refresh 方法。
 Jet 引擎给应用程序带来强大的力量。由于具有对异种数据、复杂查询的结果执行复
杂函数的能力,它大大地缩减了执行以数据为中心的操作时间。DAO 程序设计模型提供
下述功能:
■ 通用的、可访问任何 ODBC 数据库的程序设计模型──与 ODBC 规范级别无关。RDO
要求 Level II ODBC 驱动程序(或增强型 Level I 驱动程序),而 DAO/Jet 没有这
种限制。
■ 从多个 ODBC 或 ISAM 源获取及连接异种数据的能力。
■ 为作为联合产物的复杂结果集创建可更新游标的能力。
■ 无需 API 函数的面向对象的程序设计模型。
■ 允许绕过 Jet 查询处理器的传递机制。
■ 键集、静态(快照)、仅向前滚动的快照游标的实现。
■ 高级的结果集管理。
■ 通用的错误管理。
 详细信息  关于使用 Data 控件和使用数据绑定控件的详细信息,请参阅《程序员指
南》)中“访问数据”;关于特定的对象、属性或方法的信息,请参阅《联机手册》中
的《语言参考》。《客户/服务器应用程序开发指南》中“使用数据访问对象访问远程
数据库”一章,提供了使用 DAO 访问远程数据的详细信息。

★ 使用远程数据对象 (RDO) 创建客户/服务器应用程序      描述了由 RDO 接口实现
的 RemoteData 控件、绑定型控件及远程数据对象 (RDO)。
远程数据对象 (RDO) 是位于 ODBC API 之上的一个对象模型的薄层。它依赖 ODBC
 API、选定的 ODBC 驱动程序以及后端数据库引擎实现大部分的智能和功能。因此,它
短小(大约 250 K)、快速、强健。因为 RDO 同样显示出基本的 ODBC 处理方法,所
以可以直接执行大多数 ODBC API 函数。
使用 RDO 可以提交带有或不带有参数的查询,创建单一或多个结果集,或利用类似
DAO 的、独立于数据库的、面向对象的代码处理查询的结果。然而,RDO 完全依赖对类
似 SQL Server 或 Oracle 的智能型数据库服务器的访问,这是在设计时就确定的。虽
然可以使用 ODBC 驱动程序访问 Jet 和 ISAM 数据库,但是不推荐这样做。RDO 可以
访问任何 32 位的符合 Level II 规范的 ODBC 驱动程序。
RDO 包括若干对象、属性、方法,它们经过特别设计以实现同存储过程及其参数、输入
和输出参数、返回状态值的协同工作。RDO 也可处理由单一查询生成的任何数目的结果
集。这些功能体现了 DBMS 的实现,这种实现通过使用存储过程限制对基本数据的访
问。因为在这种情况下开发者没有对基本表的访问功能,所以不能使用可更新的游标。
在操作完成或即将开始时可以执行大多数 RDO 方法以通知代码,执行的方式可以是同
步(缺省)、异步,也可通过事件过程的使用。使用这些异步操作和事件过程,实际上
可以消除数据访问时常常发生的、应用程序长时间被阻塞的状况。
RDO 还包括一个支持开放式批更新的客户批游标库。在许多情况下,变更游标行时每次
只变更一行是一件耗时而低效的事情。开放式批更新允许创建结果集而且不限制更新数
目,使用 BatchUpdate 方法完成所有的变更。这样不仅减少了服务器、网络和 ODBC
的负担而且提高了性能。
需要对多个远程服务器执行相同的查询时,可以使用 RDO 将 rdoQuery 对象从其现有
的连接中分离出来,再将其与另一个连接关联。使用这种方法,就可以对一个特定的服
务器列表执行相同的查询。rdoQuery 对象与 rdoConnection 对象都可以创建为独立的
对象,这样不仅简化了编程而且允许更容易的,动态的 rdoQuery-rdoConnection 关
联。
详细信息   “使用远程数据对象与 RemoteData 控件”提供了关于使用 RDO 的全面的
指南。

★ 使用RemoteData控件创建客户/服务器应用程序。
RemoteData 控件与 Data 控件类似,唯一的不同是它创建并操作 RDO 对象,利用 RDO
和 RemoteData 控件,无需使用 Jet 引擎就可以通过各种数据识别绑定控件访问 ODBC
数据源。这样,在访问远程数据库引擎时将显著地提高性能,并且更加灵活。
RemoteData 控件可以容纳 Data 控件所容纳的数据识别绑定控件并支持相同的程序设
计接口,但它使用 RDO 而且局限于键集游标和静态游标。如果需要在外部 ODBC 数据
源使用绑定型控件而且不打算访问本地 Jet 数据库或任何由 Jet 支持的 ISAM 数据
库,那么使用 RemoteData 控件和与其关联的对象是一个可行的办法。请注意
RemoteData 控件不支持存储过程的可更新型结果集的创建。然而,在需要时可以使用
此控件的事件过程发送 Update 语句。
RemoteData 控件还支持开放式批更新和客户批本地游标驱动程序。这将允许推迟多行
更新,从而使几个操作象一个事务或工作单元那样完成。
RDO 程序模型提供如下功能:
■ 通用的,可以访问任何 32 位 Level II ODBC 数据源的程序设计模型。
■ 面向对象的程序设计模型,此模型不需要使用 API,但外部化的 ODBC 环境、连接
和语句句柄,允许在需要时使用 API。
■ 服务器端实现的键集、静态、动态与仅向前的游标、两种类型的客户端游标库以及
开放式批更新。
■ 完全异步的和事件驱动的操作。
■ 创建独立的 rdoQuery 和 rdoConnection 对象的能力,以及将查询与连接自由关联
的能力。
■ 支持同步、异步、事件驱动的连接、查询、结果集置入方法。
■ 高级的结果集管理,包括限制返回行数目、管理多个结果集、处理存储过程输入、
输出、返回值参数。
■ 为 Microsoft SQL Server 和 Oracle 远程数据库引擎优化的功能,并可以使用所
有的 Microsoft SQL Server 与 Oracle remote 数据库引擎的功能。
■ 通用的错误管理,不仅显示 ODBC 错误而且显示本地的错误和消息。
 详细信息   请参阅《联机手册》的《语言参考》中“远程数据对象和集合”及
“RemoteData 控件”。同样,“使用远程数据对象与 RemoteData 控件”是关于使用
RDO 访问远程数据的更全面的指南。

★ 使用ODBC API 创建客户/服务器应用程序      概述了 ODBC API 程序设计模型。
     ODBC API 是对 Open Database Connectivity 库和驱动程序的调用级接口。不仅
对于 Microsoft SQL Server,而且对于任何其它提供 ODBC 驱动程序的数据库──与
规范级别无关,ODBC API 都提供数据访问的连接性。通过对此接口的编程,可以创建
独立于数据库的代码,这意味着 ODBC API 将提供自动适应各种数据库的、通用的程序
设计模型。
注意   除 ODBC 驱动程序管理器提供的函数以外,ODBC API 经常被由驱动程序本身提
供的函数所扩充。例如,Microsoft SQL 服务器驱动程序提供的若干函数,它们在驱动
程序范围之外尚无标准。虽然这些函数可以利用某些特定的驱动程序功能,但它们在其
它驱动程序中可能不被支持。如果应用程序将在多种后端系统使用,则应小心地使用这
些扩展功能或者根本不用。
 影响开发者选择 API 方法的因素有许多。这其中包括追求更高性能、对接口更精细的
控制、更小的内存占用。然而,当新工程开始时,如果起初的设计需要一种 API 方
法,应权衡再三后再使用 ODBC API。这是因为,虽然某些数据访问引擎(包括
Microsoft SQL Server )采用 ODBC API 作为它们的本地数据库接口,但是开发、调
试、支持 ODBC API 是非常困难的。Visual Basic 与动态连接库在支持
Unicode-to-ANSI 字符串转换的 32 位操作系统(如 Win95)上交互作用的方式、列数
据类型绑定以及其它问题,使事情更加复杂。与其它程序设计接口不同,ODBC API 并
没有象 DAO、RDO 或 VBSQL 一样作成“防弹型”或者是以 Visual Basic 为中心的,
所以它极易触发致命的一般性保护错误(GPF)。它是这里提供的唯一没有特别适应
Visual Basic 的接口。
注意   ODBC API 与Visual Basic Version 2.0 提供的 ODBC 接口差异很大。ODBC
API 在 Visual Basic 2.0 内部使用,以提供第一个数据访问对象。这些对象、方法、
属性与 Visual Basic Version 5.0 提供的全面的 DAO 实现迥然不同。
 ODBC API 提供对错误和消息的全面控制,而且允许使用大多数后端数据库的功能。
ODBC API 不支持数据识别绑定控件,但它支持自身的、利用客户端或服务器端游标的
结果集。创建、管理游标及其它操作的责任或由客户的 ODBC 驱动程序承担,或者由服
务器引擎本身承担,这取决于驱动程序的规范级和远程服务器的容量。
ODBC API 程序设计模型提供如下功能:
■ 可以访问众多数据库体系结构的通用的程序设计模型。
■ 与 Microsoft SQL Server 的本地接口。
■ 与 VBSQL 相比,需要更少的 API 函数(但是,这些函数比较复杂)。
■ 存储过程参数的智能化处理。
■ 对 Microsoft SQL Server 6.0 版服务器端游标的支持。
■ 键集、动态的、静态的、仅向前的游标的实现。
■ 高级结果集管理。
■ 可以使用几乎所有 SQL Server 功能。
■ 完全的调用后错误管理。
 图 9.3 说明了 Visual Basic 如何执行 ODBC API 代码──后者直接操作 ODBC 驱动
程序管理器入口。这些入口与允许访问特定类型远程数据库并且实现低级数据访问操作
的驱动程序连接。为访问不同类型的数据库,通常不需要重新编写应用程序,而仅只需
变更远程数据库驱动程序。


图 9.3   使用ODBC API访问远程数据库引擎






ODBC API 是 Jet 数据库引擎用于访问外部非 ISAM 数据库的接口。虽然存在 ISAM
ODBC 驱动程序,但 Jet 使用本身的可安装的 ISAM (IISAM) 驱动程序访问这些数据
库。
详细信息  “使用 ODBC API”提供了更详细的,包括示例代码的信息。同时也请参阅
Microsoft 的 ODBC 2.0 Programmer's Reference and SDK Guide。

★ 使用 VBSQL (DB-库) 创建客户/服务器应用程序     概述了 VBSQL API 程序设计
模型。
Visual Basic Library for SQL Server (VBSQL) 是为 Visual Basic 和 Microsoft
SQL Server 特别设计、调整的 DB-库 API 的实现。VBSQL 提供了几乎所有的 DB-库
API 并被支持为 Microsoft SQL Server 本地接口。
注意   如果创建新的客户/服务器应用程序,并需要 API 模型的灵活性与高效性,则
应该使用 RDO 而不是 VBSQL 或 DB-库。
 当应用程序仅仅需要访问 Microsoft SQL Server 系统时,请使用 VBSQL。另外,
VBSQL 也适用于某些旧版本的 Sybase SQL Servers。然而,本书的重点在于
Microsoft SQL Server。
VBSQL 与 ODBC API 相似之处在于:它们都提供最广泛的灵活性,而且在某些情况下,
都提供比 RDO 更好的对 SQL Server 的访问。VBSQL 不支持绑定控件,所以应用程序
必须提供代码以置入和维护基于窗体的控件。两种 API 程序设计模型的一个重要不同
是:VBSQL 模型是特别为 Visual Basic 开发的,所以它将开发者、应用程序与许多由
直接访问基于 C 语言的 API 所带来的、固有的危险隔绝起来。VBSQL 不要求使用
Unicode 翻译或列绑定代码。
VBSQL API 程序设计模型支持如下功能:
■ 使用几乎所有的 SQL 功能──除了两阶段提交。
■ 完全的异步操作。
■ 对 Microsoft SQL Server 的访问──但无法访问任何其它的后端数据库引擎。
■ Microsoft SQL Server 的本地接口。
■ 完成各种 SQL Server 功能的特定的 API 函数。
■ SQL Server 6.0 版支持的服务器端游标。
■ 键集、动态、静态、仅向前滚动的游标实现。
■ 'N' 行缓冲结果集。
■ 成批复制 (BCP) 接口。
■ 全面的、中断驱动的错误、消息处理程序。
 注意   Microsoft SQL 服务器支持 VBSQL 控件和其它程序设计部件。
 DB-库通过可替换的网络驱动程序将私有数据传递给 SQL Server。为了使 DB-库返回
错误和消息,应用程序必须包括可生成回调事件的 VBSQL 自定义控件。SQL Server 工
作站版中提供的 Microsoft SQL Server 程序员工具包包含了 16 位和 32 位的 VBSQL
控件。
详细信息   请参阅 SQL Server 6 程序员工具包提供的 Visual Basic Library for
SQL Server。同样,“使用 VBSQL API”是关于使用 VBSQL 访问远程数据的更全面的
指南。
注意   Microsoft SQL Server 程序员工具包提供支持 VBSQL API 的 Visual Basic
部件。

 三.客户/服务器系统资源管理介绍若干设计问题,确保应用程序的高效性能并被用户
认可。
数据访问前端应用程序是大型数据库管理系统 (DBMS) 中的单一部件。在 DBMS 的综合
性能中,前端经常扮演重要的角色。因此,认真考虑它对客户/服务器系统资源的影响
是极为重要的。当许多用户使用应用程序时,这一点格外突出。
在设计客户/服务器系统时需要考虑的其它重要因素还有:数据库的设计、配置和实
现,服务器、客户机、以及连接这些部件的网络。尽管代码编写的很完美或程序设计模
型实现的很成功,如果不能恰当地设计、实现数据库,那么其性能也无法保证,用户对
应用程序也难以认可。
忽略这些,将导致一些不易发现的问题:
■ 需要增加 RAM 或磁盘容量。
■ 网络容量增加的需要。
■ 客户机数量的不足。
■ 等待时间的增加。
■ 较低的系统综合工作效率。
■ 用户失败的增加。
 下面的主题讨论必须处理的系统资源因素──这些因素最好在设计规范确定之前处理

★ 客户/服务器 DBMS 容量计划   概述管理驻留于客户或服务器上的共享资源的问
题。
某些程序设计技术倾向于过多地使用诸如中央处理单元 (CPU) 时间、连接、磁盘空间
或 RAM 之类的系统资源。这些共享的资源可以驻留在客户机,这里的程序与其它基于
Windows 的应用程序共享资源,也可以驻留在服务器,这里的资源短缺将影响所有的服
务器的操作。
客户/服务器应用程序的目标之一是有效地管理资源请求和减少对于 DBMS 系统整体的
影响。如果某个应用程序创建了过多的连接或导致了过多的网络通信量,系统性能将受
到损害。在这种情况下,DBMS 可支持的活动的用户数目(应用程序的实例)将受到限
制。
游标的巧妙使用
导致系统超载的另一个因素是游标的不正确使用。范围太广的游标同样会使 DBMS 过
载。使用 SQL Server 6.0 版提供的服务器端的游标可以把负担转移到中心服务器,从
而缓解了网络通信量。然而,这增加了服务器的处理负担和远程服务器临时工作区容量
要求。所以更好的解决办法是限制结果集,使其与需要的数据相符。
提示   开发者将数据库从平面文件方法升迁到关系系统时,最常犯的错误是不改动应
用程序就简单地将表转移到远程服务器。这样就把远程服务器降低为文件服务器,从而
大大降低了性能和应用范围。在许多情况下,基于 ISAM 的应用程序必须有效地重新设
计,否则将不能适合大型系统──特别是当系统包括大量的用户时。
作为一般规则,应用程序不应直接打开表。虽然这对于 ISAM 数据库是合法的,但是这
种技术将严重地限制系统性能,降低为附加用户提供服务的能力。避免对基本表创建游
标──特别是那些选择表中全部或大多数行的游标。作为替代,应使用一次返回一个较
小行子集的存储过程或查询;在大多数情况下,200 行是上限。
增加系统容量
为补偿可执行程序给系统带来的负担,也许需要增加系统的容量。这可能包括增加服务
器、客户工作站及网络的容量,即添加更多的 RAM 或硬盘,或简单地添加速度更快的
处理器或网络接口卡。然而,这是短期的解决办法,因为随着活动用户的增加,由应用
程序引起的性能问题将大大增加。因此,在选择数据访问程序设计模型时,应仔细考虑
接口能力以节约系统资源。

★ 客户/服务器数据库大小的考虑   概述大小问题,包括页锁定、结果集的限制以及
数据变更的代价。
数据库的大小应作为程序设计模型抉择中的重要因素。较小数据库的共享页也相对较
少。因此随着活动用户的增加,公共页被另一个用户锁定的情况非常多。即使在大型数
据库中,最经常被锁定的页还是那些最经常被访问或更新的页,尽管它们只是整个数据
库的一个很小的子集。所以,接口应该支持灵活的锁定方案,允许应用程序对各种锁定
状态作出反应。同时,还应考虑数据库引擎解决死锁状况的对策,这种状况发生在:一
个用户锁定了数据页,并试图锁定另一个被第二个用户锁定的页,而第二个用户也正在
试图锁定已经被第一个用户锁定了的那页。
大型数据库需要与适合小型数据库的策略截然不同的数据访问策略的实现。例如,对一
个具有一百万行的表,试图打开不带限制性 SQL WHERE 子句的游标是不明智的。块更
新或其它影响许多行的操作也会给锁定策略和事务日志管理带来显著的影响。
实际上,执行数据库块更新动作查询要比在客户端处理大量取回行的效率高的多。客户
端重复性操作除了带来容量和性能问题外,还要处理数据的重新同步。当需要在远程服
务器变更数据时,应创建一个存储过程并使用此过程的逻辑执行操作。这些过程不仅易
于编写,而且它们执行速度较快,带给客户机与网络的负担也较少。
随着远程数据库大小的增加,应考虑变更数据的代价。无论选择何种查询处理器,都应
仔细斟酌事务或批操作的使用。对于有许多索引的数据库,每次更新都需要索引的变
更。在远程服务器维护事务日志时,更新操作可能将日志充满──这就需要立即的维
护。另一方面,附加的索引将大大地提高取数据的性能。

★ 设计多用户客户/服务器应用程序   介绍在为多用户设计时需要考虑的概念。
随着多用户数据库用户数目的增加,处理用户资源请求的复杂性随之增加──在某些情
况下,这种增加是呈指数型的。每个用户(应用程序的实例)激活 n 个连接,每个查
询锁定 n 页并创建 n 行游标信息,每个操作生成 n 块网络通信量。由此可以容易地
看出,在没有考虑到真正多任务的严酷性的系统上,系统资源如何被过分地使用。
评价数据访问程序设计模型应基于活动的用户而不是潜在的用户。活动的用户实际打开
了连接并执行查询和更新。潜在的用户是那些可能连接,但目前还没有这样做并且不大
可能这样做的用户。考虑到计算系统资源的限制,进行容量计划时,应决定一个活动用
户与潜在用户之间合理的比率。设计一个支持 30% 或更少的潜在用户的系统并不少
见。然而,如果应用程序在完成访问数据库之后不释放资源,那么这个比率是不能接受
的。
为了处理因系统资源耗尽而导致的情况,设计中还应包括足够的错误恢复。所有的应用
程序设计都可能需要建立附加的连接,或者使用那些可能导致系统过载的资源。程序设
计模型如何处理这些意外情况是设计的决定因素。
附加用户登录后,数据库服务器仍能保持较高性能的条件是:它们能够高速缓存那些共
同使用的过程和逻辑数据。例如,在附加用户登录进入 Microsoft SQL Server 之后,
它们可以访问那些已经被其它用户访问的、高速缓存区中的存储过程和逻辑数据页。如
果某个过程或数据页已经在内存中,附加用户可以立即使用它们──无需从磁盘读入。
客户/服务器系统可以支持的用户总数与每个可执行程序使用的系统资源成直接比例关
系。资源不是被客户消耗──它们被借用。当用户退出或关闭游标后,应尽快释放计算
机的资源,使得其它处理过程可以使用这些系统资源。
如果需要,则应限制一个应用程序在某台计算机或整个客户/服务器系统中启动的数
目。也许还需要这样的例程:在试图创建应用程序的另一个实例之前,特别是当应用程
序需要多个正常运转的连接时,此例程查询可以使用的连接数目。虽然如此,应用程序
还是应该精心设计,以使得在连接无法建立或当连接在执行中丢失时,应用程序仍然能
够继续运行并能恢复正在管理的数据。
在文件服务器系统中将硬盘页缓存于 RAM 中是可行的。但是,如果系统意外地崩溃,
数据库的完整性将被严重地损害。为避免这个问题,应确保所有的变更都立即写入磁
盘。
同时,也应避免这种设计:它使用多个 Data 或 RemoteData 控件,而且这些控件在应
用程序首次启动时就被启动。例如,当 Data 控件在 Form_Load 时刻启动后,它将消
耗两个连接,这以后的每个附加的 Data 控件都将再消耗一个连接。因此,四个 Data
控件将至少消耗五个与远程服务器的连接。在一个至多有十个连接的系统上,两个应用
程序就将耗尽所有可用的连接。需要牢记的是,在 Close 方法执行后,DAO 并不释放
连接,而是将它缓存以备在近期内再次使用。除非通过正确设置 ConnectionTimeout
参数的方法取消连接,否则应用程序将在无意中耗尽所有可用的连接。
提示   另一个常常被忽视的系统资源是用户的耐心。在可能的情况下,不使用那些强
迫用户在见到请求信息之前必须等待的程序设计方法。例如,在显示给用户结果集的 n
行之后,使系统在后台完成查询。

★ 带有视图和存储过程的客户/服务器的设计   提出了限制返回客户机的结果集的提
示。
将平面文件系统中的数据表复制到远程服务器,并将这些表与现有的 Jet 数据库链接
(或附加),是迁移数据库最常见的一种方法。然而,对于那些希望以后扩大的大型
DBMS 数据库和小型数据库,这种方法不是很有效。ISAM 模型非常依赖对表(或文件)
的访问。虽然客户/服务器系统仍然以逻辑表为基础,但是很少允许应用程序访问这些
基本表──因为这样效率太低,而且很危险。在大多数情况下,系统管理员通过 SQL
视图或存储过程(更常用)提供访问数据库的途径。
让我们深入到 ISAM 应用程序之中,看得更明白一些。在与 Jet 一起使用时,Visual
Basic 数据访问对象 (DAO) 严重地依赖游标,而且它对存储过程的支持也很有限。很
多情况下,应用程序试图将表数据(有时是所有的表数据)取到工作站,然后再以动态
集或快照类型的 Recordset 对象形式处理。
例如,在应用程序执行 SELECT * FROM TABLE 查询时,它实际是请求查询处理器将表
的整个内容取到工作站并建立键集或数据备份。在大型客户/服务器应用程序中,这种
策略不能工作。
如果不重新设计应用程序而仅仅把 Jet 表转移到类似 SQL Server 的远程服务器,不
会提高任何性能。客户/服务器系统远远不只是一个文件/表服务器。它是能够接受智能
查询的数据仓库,能尽快地回答所请求的特定数据。然而,不幸的是,大多数智能型后
端被用成了文件服务器。

★ 在远程数据库执行动作查询   概述了动作查询,它执行变更基本数据的操作或者完
成管理性的操作。
并非客户/服务器前端应用程序执行的所有操作都会返回行,有的甚至连返回状态都没
有。一个没有返回数据,只是简单地执行数据库操作的查询被称为动作查询。例如,动
作查询可以执行变更基本数据的动作或完成诸如添加新表、添加新用户之类的管理性的
操作。
实际情况是,由数据库引擎执行的动作查询来处理基本数据的变更,有较高的效率。如
果一个策略需要一行一行地变更结果集中的单个行,应该重新评价它。
发送只涉及一个数据行的动作查询,效率很低。RDO 支持执行批更新操作,从而提高了
性能。当某个数据库变更操作必须与另一操作结合进行时,应用程序应该为这些操作创
建事务。例如,当必须对某一帐号发出“credit”命令,而对另一相关帐号发出
“debit”命令时,那么在这些操作之前的代码中应包括 BEGIN TRANS 语句并应以
COMMIT TRANS 语句结束操作。只有当 COMMIT TRANS 语句发出后,数据库引擎才执行
操作集合。
同时,还可以通过事务强制使用多语句批处理,这样可以提高网络与引擎的效率。如果
需要插入或更新大量的行,而在输入/输出或块复制设施之中有一个不适用,那么应该
考虑将这些操作组合在一个事务中。
在 SQL 语法支持多语句执行的情况下,应考虑将插入、更新、删除语句组合在有 n 个
语句的批中,然后将批发送到服务器并以单个操作的形式处理。一个批中可以包括的最
佳语句数取决于系统,但一般应在 10 到 50 之间。通过将若干操作结合在一起,可以
减小系统花费在单个语句上的开支。然而,如果这其中的一个操作失败,整个集合将回
滚并必须重新处理。

★ 客户/服务器网络设计的考虑   概述减少网络通信量可采取的步骤。
在客户/服务器体系结构中,前端通过某种网络与服务器连接。在任何客户/服务器的成
功设计中,网络的类型和容量都扮演关键性的角色。例如,如果客户计算机通过含有低
速链接的广域网 (WAN) 与中心服务器连接,则网络预计处理的数据量将影响程序设计
模型的选择。
另外,在何处处理查询同样显著地影响网络负担。每个数据访问程序设计模型,都提供
了对如何处理查询以及在何处处理查询的不同程度的控制。通常,鼓励数据访问引擎在
服务器──数据存放之处执行查询。如果数据库在网络中传输整个的表或大型游标键
集,或者试图保持 WAN 的同步,则将引发性能问题。
讨论的程序设计模型,都以尽量减小网络通信量为目的。如果 RDO、VBSQL 和 ODBC 模
型能够和服务器端的游标一同实现,那么它们的效率更高。服务器端游标在服务器而不
是在客户端创建游标键集,这样就减少了网络中传输的多余数据。使用 RDO 提供的开
放式批更新功能,也可以显著地减少网络通信量。因为这种技术将多个更新组合成单一
的 ODBC 操作。
所有的程序设计模型都提供了创建非游标集合的功能,这种非游标集合可以用最小的开
支传输请求的数据。在使用 SQL 查询请求最小数据量的小型结果集合上,非游标操作
表现最为出色。如果小心地限制网络通信量,那么这种方法适合在 WAN 使用,而且也
适用于极低速(2400 波特)的调制解调器链接。
在通过 WAN 建立连接时,与远程服务器的链接花费的时间可能会显著地增加。连接时
间也依赖于程序设计模型、访问方法的选择、以及数据库引擎处理外部数据的方法。例
如,通过 ODBC 使用 DAO/Jet 直接打开数据库所花费的时间将远远大于打开附加表或
使用 API 模型所花费的时间。(关于每个程序设计模型大约花费的连接时间,请参阅
此章中“数据访问选项总结”的表 9.1)。
丢失 WAN 连接是经常让人担心的事情。在许多情况下,连接(或者,也许是数据库的
完整性)的稳定性就如本地电话系统一样。设计时应考虑 WAN 或 LAN 连接的丢失,将
其作为综合错误处理例程的一部分。
对于有些程序设计模型,突然丢失连接即意味丢失数据或特定连接临时对象,所以应当
开发错误处理程序以处理这种意外。特定连接临时对象是由远程服务器在为某个应用程
序实例分配的空间中创建的对象(例如,服务器端游标,临时表或过渡性的结果集)。
一旦应用程序或数据库接口释放它的连接,这些对象将不再存在。
详细信息   关于使用特定连接临时对象的详细资料,请参阅此章后面的“建立客户/服
务器连接”一节。

★ 客户/服务器应用程序内存占用的管理   概述了 Microsoft 分布式事务协调器 (MS
DTC),一种分布式事务设施。
一个可执行程序的内存遗迹是指运行该程序所需的 RAM 的大小。虽然,随动态链接库
(DLL)、窗体、控件的加载、卸载,应用程序的内存需要会发生变化,但是在不过度使
用交换的情况下,确定运行应用程序所需的内存数量是重要的。每当内存页转移到磁盘
或从磁盘加载时,处理速度将会变慢。如果计算机有足够的 RAM 以至所有需要的页都
可在内存中驻留,则交换实际上将不需要。
如果客户计算机的 RAM 数量有限,则应计算所需 DLL 占用的内存数量。在某些情况
下,结果集使用的 RAM 数量也是可执行程序内存遗迹的一个因素,但是对这个因素,
可以通过应用程序设计和数据访问方法加以更多的控制。

★ 分布式事务管理(DTM)的实现
Microsoft SQL Server 6.5 中的程序设计部件使您能够开发出复杂的客户/服务器应用
程序,这些应用程序可以利用运行 Microsoft Windows NT 和 Microsoft Windows 95
操作系统的工作站上的 Microsoft SQL Server 的强大功能。
SQL Server 6.5 包括 Microsoft 分布式事务协调器 (MS DTC),后者为 Microsoft
Windows NT 和 Microsoft Windows 95 操作系统提供了强健、高效、可缩放的、易于
使用的分布式事务设施。MS DTC 接受了处理软件部件分布集合事务的挑战,这些部件
存在于网络中。MS DTC 完全与 Microsoft SQL Server 融为一体,并且每台管理分布
式事务的计算机的 SQL 企业管理器都提供了事务管理器。
MS DTC 提供了一个管理接口,通过此接口可以启动或停止 MS DTC、查看当前的 MS
DTC 数据和跟踪记录、强制 in-doubt 事务的结果、以及设置各种全局 MS DTC 选项。
应当将一个事务想象为单一变更事件,或者发生、或者不发生。设想您希望将一个银行
帐户的钱转移到另一个帐户中。无论基本的机制是什么,您希望确认:或者两个帐户都
变更了,或者两个都没有变更。在分布式系统中,完成这件工作很困难──计算机可能
出故障,信息可能丢失。所以,事务提供了一种方法,将一组操作绑成单一的原子执行
单元。
事务的基本原则是:几个独立的实体都同意提交或完成操作。如果有一方不同意,就将
放弃整个事务。一旦所有的实体都同意,就可以提交事务。MS DTC 为其它部件充当协
调事务的角色。
在 MS DTC 术语中,指挥者被称作事务管理者。事务中提供事务保护资源(例如关系数
据库)的参与者被称为资源管理者。
应用程序通过调用事务管理器的 BeginTransaction 方法启动一个事务。这将创建一个
代表该事务的事务对象。此后,应用程序调用资源管理器完成事务的工作。
应用程序对每个资源管理器的调用表明了应用程序当前的事务。例如,如果应用程序正
在使用关系数据库,它将调用 ODBC 接口,而此接口将把事务对象与 ODBC 连接相关
联。从这以后一直到事务结束,所有在那个连接上发出的数据库调用都代表这个事务执
行。
当资源管理器第一次代表事务工作时,它通过调用事务管理器在事务中登记。随着事务
的进行,事务管理器跟踪每一个登记在事务中的资源管理器。
典型的情况是,应用程序通过调用 Commit 方法完成事务。如果应用程序无法完成事
务,它将调用取消事务的 Abort 方法。如果应用程序失败,MS DTC 将取消事务。
在应用程序成功地完成事务工作并调用 Commit 方法后,它将调用 MS DTC 提交事务。
MS DTC 通过一个两阶段的提交协议获得所有需要提交的已登记的资源管理器。两阶段
的提交协议保证所有的资源管理器都提交了事务或者都取消了事务。在第一阶段,MS
DTC 询问每一个资源管理器是否准备提交。如果所有的参加者都回答“是”,则在第二
阶段 MS DTC 就向它们广播提交消息。如果事务的任何部分失败,或某一资源管理器没
有响应准备的请求,或者某一资源管理器回答“不”,那么 MS DTC 通知所有的资源管
理器事务取消。
MS DTC 和部件对象模型
MS DTC 是部件对象模型 (COM) 体系结构(支持 OLE 和 ActiveX 的基本的体系结构)的
一部分。事务管理器是大多数数据库系统的关键部分。它也是某些操作系统可选择的部
分。Microsoft 相信对于分布式应用程序,事务是关键的。因为它提供了模块化执行,
这补充了 COM 的模块化程序设计。所以,Microsoft 为 Microsoft Windows 95 和
Microsoft Windows NT 操作系统提供了事务管理软件。
在其首次发行中,MS DTC 与一个资源管理器协同工作:Microsoft SQL Server。MS
DTC 实现 OLE 事务接口。所有的 OLE 事务接口都是公共的,这样任何资源管理器都可
以成为 OLE 事务资源管理器。将来,Microsoft 与其它软件公司将添加其它的事务型
资源管理器,例如分布式对象系统、事务型文件系统、事务队列系统以及工作流管理系
统。
用 Visual Basic 编写的应用程序无法直接启动和控制 MS DTC 事务。Visual Basic
应用程序可以通过调用能够启动和控制 MS DTC事务的 Transact-SQL 存储过程来使用
MS DTC 事务。

 四.客户/服务器数据访问设计问题概述了影响应用程序设计的一些数据访问问题,与
选择何种应用程序模型无关。
全面理解客户/服务器典范有助于成功实现远程引擎数据库的前端。没有客户/服务器开
发经验的开发者很容易犯下根本性的错误,导致应用程序的失败或应用程序的性能十分
不理想。这些失败可能带给他们错误的客户/服务器的概念,或是屏弃那些用来配置系
统的开发部件。
例如,所设计的表包含过多列或没有正确的标准化,将失去许多关系数据库模型的优
点。取整个表或是创建包含过多行的游标,这样的设计同样会导致令人失望的性能──
无论选择何种程序设计模型。
中心服务器系统前端的程序设计──特别是设计那些访问大量数据的系统或者是具有几
十到几百用户的系统──与平面文件 ISAM 数据库系统的程序设计迥然不同。开发一个
访问远程数据库的前端时,应该提供一个精心设计的关系模型。也许您想推迟它,直到
后来试图提高系统性能时才去做,但是这样非常不合理,就好象盖好了房子上面的一层
后又想更换地下室一样。
在决定采用哪种程序设计模型前,应该注意若干问题,这些问题会影响设计并与选择那
种模型无关。
下面的主题简略地介绍了每个问题。可以在下一章“数据访问选项”中找到针对于每种
程序设计模型的实现示例、细节和技术。
★ 建立客户/服务器连接   概述了关于本书描述模型的面向连接的问题。
为获取远程服务器的访问,应用程序必须建立连接。连接在网络的服务器、客户之间创
建通信链接。此连接的句柄将传回应用程序。一旦创建,连接将提供对特定服务器及服
务器上某个数据库的访问,它也能提供对应用程序在服务器上创建的任何特定连接临时
对象的访问。
特定连接临时对象的使用
在许多情况下,在数据库服务器上创建的临时对象属于特定的连接──换句话说,它们
属于应用程序特定的实例。这些临时对象包括结果集、基于服务器的游标、正在工作的
保存区域、临时表。连接一旦关闭,服务器自动释放这些对象。因为这些临时对象只通
过特定的连接提供给应用程序的特定实例,所以除全局临时对象外,其它连接无法看见
或访问它们。对于数据库服务器的其它用户,全局临时对象可以创建且是可见的,当该
对象的最后一个用户中断连接时,对象将被释放。如果应用程序依赖这些对象的持久
性,则应使用某种连接策略,这种策略在仍然需要对象时不武断地关闭连接。
建立客户/服务器连接
根据选择的客户/服务器数据访问程序设计模型的不同,可以使用下表中描述的方法在
代码中建立与远程服务器的连接:
程序设计模型   连接技术

DAO ODBC 通过 Jet 打开 Jet 数据库,它的表附加到或连接到 ODBC 服务器数据库
表。
DAO ODBC 通过 Jet 使用包括 ODBC SQL 传递查询的 DAO QueryDef 对象。
DAO ODBC 通过 Jet 打开直接引用 ODBC 数据源名称 (DSN) 或直接引用正确的 ODBC
服务器和驱动程序的 DAO Database 对象。
DAO ODBC 通过 Jet 使用引用附加表或 ODBC DSN 的 Data 控件。
DAO ODBCDirect  使用引用 ODBC DSN 或引用 ODBC 服务器和驱动程序的 Data 控件。
DAO ODBCDirect 打开引用 DSN 或引用正确的 ODBC 服务器和驱动程序的 DAO
Connection 对象。
RDO 2.0 使用 RDO OpenConnection 方法,这种方法引用 ODBC DSN 或引用特定 ODBC
服务器和驱动程序。
RDO 2.0 使用 RDO EstablishConnection 方法,这种方法引用 ODBC DSN 或者引用独
立的 RDO Connection 对象的特定 ODBC 服务器和驱动程序。
RDO 2.0 使用引用 ODBC DSN 或引用特定的 ODBC 服务器和驱动程序的 RemoteData 控
件。
ODBC API 通过 SQLConnect 或 SQLDriverConnect API 函数创建引用 ODBC DSN 的
ODBC 连接句柄。
VBSQL 使用 VBSQL SqlOpen 或 SqlOpenConnection API 函数打开与特定 Microsoft
SQL Server的 DB-库的连接。
 所有这些技术都创建一个或多个连接,将应用程序的特定实例与远程服务器链接。在
某些情况下,如使用 Microsoft SQL Server 工作站版,开发者可使用的连接数目相对
较少,为 15 或更低。在其它情况下,如使用 SQL Server 企业版,数目则可以相对较
高(32,767)。并非所有这些方法都产生相同的性能。例如,对时间和网络资源来说,
使用 DAO 直接打开远程数据库是昂贵的。
注意   请向服务器管理员查询可用连接的数目。许可证或资源的制约将限制这些连接
的数目。因为每个应用程序都可创建任意数量的连接,考虑到远程服务器处理连接的能
力,所以它所支持的用户数目将受到限制。
 对应用程序来说,保持与远程服务器不变的连接可能是不必要的,但是在某些情况
下,丢失连接意味着丢失特定连接临时对象。在一个多用户系统中,连接管理非常重
要,这是因为每个应用程序使用的连接数目将限制连接在数据库上的用户总数。根据服
务器和可用连接数目的不同,可能会发现在应用程序的生命期内保留一个或多个已建立
的连接是有效率的。在其它情况下,因为连接句柄数目有限,所以可能需要尽快地打开
和关闭连接以满足其它用户。选定的程序设计接口一定要支持合适的连接策略。

★ 客户/服务器数据库安全性问题   概述了关于本书描述模型的面向安全性的问题。
每个数据库引擎都提供一个安全模型,此模型限制对它所管理的数据的访问。这些安全
系统,无论简单还是复杂,都是为防止未授权的用户访问敏感数据而设计的。
由于服务器的连接决定应用程序用户的身份,所以打开连接的过程在建立希望访问的数
据库的同时,也建立了数据库服务器用户 ID 和密码。应用程序可以询问用户的 ID 和
密码,也可让操作系统和服务器通过集成的安全方案协调服务器的安全访问。
使用标准的安全机制
标准安全机制对所有连接使用 SQL Server 自身的登录验证过程。使用标准安全机制的
远程数据库服务器,要求应用程序在连接过程中提供用户 ID 与和相关的密码。用户
ID 和密码可从授权用户或应用程序本身获得。这个用户 ID 标志一个服务器的合法用
户并授权或限制对数据库中特定部件的访问。例如,一个特定的用户可能被授权访问雇
员表,但是无权访问工资表。
在非 SQL Server 系统中,如果需要 SQL server 提供的附加安全机制,应用程序应该
从某个用户捕获一个用户 ID 和密码。这些值将传递给数据库接口,在那里它们被包括
在用来建立连接的元数据中。元数据是专有的数据流,数据库接口的动态链接库使用它
们与远程服务器通信。
也许不希望每当应用程序运行时都要提示用户输入密码。那么,可以使用下面任何一种
方法:
■ 使用一个 .ini 文件维护用户 ID 和密码。如果使用这种方法,请注意将密码加密
──否则密码极易受到损害。
■ 将用户 ID 和密码放在系统注册表中。同样,应加密密码。
■ 限制通过其它方法访问应用程序。将可执行程序放置在只能被选定用户访问的网络
共享处。在这种情况下,用户 ID 和密码可以被嵌入应用程序中。
■ 在应用程序中使用硬编码的用户 ID 和密码。对那些功能被严格管理并且不能执行
用户定义查询的应用程序,这种方法足够了。
使用域管理安全机制
SQL Server 4.2 版及以后的版本和其它基于 Windows NT 的服务器允许使用域管理安
全体制方案。当包括 SQL Server 的 Windows NT 域通过本身的安全系统打开对服务器
的访问时,域管理安全体制生效。能够访问 Windows NT 域的用户就可以访问 SQL
Server,SQL Server 能够识别用户 ID。这样就不需要在 Windows NT 和 SQL Server
上保持两对用户 ID、密码。
在 Microsoft SQL Server 上设置的安全体制模式决定了服务器是否使用 Windows NT
域工作站用户 ID 和密码。SQL Server 的管理员可以设置三种模式,见下表。
安全体制模式  描述

标准的  SQL Server 用户的 ID 和密码必须由应用程序提供。不使用 Windows NT 客
户标识。SQL Servers  4.2 版之前的版本一律使用这个选项。
集成的   SQL Server 用户 ID 是从 Windows NT 域用户 ID 及密码获得。
混合的  除非客户提供用户 ID 和密码,否则 SQL Server 用户 ID 将从 Windows NT
域用户 ID 及密码获得。
通过 SQL 对象限制访问
另一种安全体制方法使用诸如视图、存储过程一类的 SQL 对象限制对 SQL Server 的
访问。只有以前定义过的 SQL 视图才被显露给应用程序。可以通过在基本表设置 SQL
权限将访问限制于特定的用户之中。应用程序象访问表一样访问视图并通过存储过程更
新数据库。SQL 服务器权限方案管理对这些视图和存储过程的访问。许多共同开发机构
偏爱这种方法,因为它提供了最高程度的安全并且最易于实现。因为它强制在服务器执
行某些复杂的联结,所以有些情况下,它具有更好的性能。

★ 客户/服务器数据访问结果集合的创建   概述结果集管理。
游标是一个指针,它指向从数据库中提取的、满足特定条件的数据集合。游标将协助用
户浏览逻辑信息集合──也称作结果集。读/写游标还支持单个行的更新。游标可用指
回基本表行的键集定义,也可用下载到客户计算机的实际数据行定义。根据创建游标时
使用选项的不同,游标可以随意滚动,也可以只允许从当前记录点滚动到结尾。
Jet 动态集类型的 Recordset 对象是 Jet 游标的例子。在《数据访问对象指南》 中
描述的许多数据访问策略都涉及游标,使用 ISAM 数据库时就已经很熟悉的策略中许多
也涉及游标。然而,许多应用程序没有使用游标,因为游标不是检索数据的最好办法。
可创建多种游标:
■ 没有行。
■ 某些或所有的行在一个单一的表中。
■ 某些或所有的行都来自公共字段逻辑联结的表。
■ 游标级或字段级只读或可更新。
■ 自由滚动或仅向前滚动。
■ 游标键集位于客户或服务器。
■ 缓冲的、高速缓存的、索引的键集。
■ 使用键集或使用实际数据。
■ 具有静态或动态的成员。
 例如,用户可以创建包含给某个顾客选定清单的游标。游标一旦创建,用户就可以通
过特定模型游标 Fetch 和 Update 命令快速地检索、更新游标行。虽然每个程序设计
模型使用不同的语法和途径实现游标,但是它们在许多方面类似。
应该只请求应用程序需要的游标选项。除静态、只读、仅向前滚动的、无缓冲的游标
外,每个附加的选项都会有某种代价──或者是客户内存、或者是网络负担、或者是性
能。在许多情况下,缺省选项生成的游标远比实际所需游标复杂、昂贵。
通常,游标不具有对其所有成员执行块更新或块删除的能力。相对而言,所有 Visual
Basic 数据访问程序设计模型都提供的数据库引擎──能使用 WHERE 子句指定相等结
果集并执行游标行的块操作。
没有正确使用游标是影响数据访问性能的根本原因。使用 ISAM 数据库时,前端应用程
序打开基本表,一个一个处理记录的情况很常见。因为除了那些最基本的数据访问操
作,常常没有“数据库引擎”执行任何其它的操作,只能由应用程序完成该任务。在与
远程数据库一同工作时,网络访问和共享数据的限制突出了与游标相关的问题。要想实
现更好的性能和程序的稳定性,理解数据库引擎的角色和如何最大程度的利用它的功能
至关重要。
应该使用数据库引擎执行块操作。当所涉及的行影响其他用户时,这类操作就应推迟到
非高峰期进行。在执行块操作时,使用事务和更合理的锁定策略同样能够提高综合系统
性能。
SQL 是检索、操作、合计数据的强有力语言。除此之外,象 Microsoft SQL Server 这
样的智能型服务器提供功能强大的标准 SQL 扩展,这些扩展支持更复杂的操作。使用
存储过程或视图在服务器中执行联结或完成数据修改,比在客户计算机本地处理检索回
行更有效。

★ 游标范围的定义   概述范围定义和它与性能的关系。
当需要游标时,仔细地定义数据的范围是非常重要的。虽然方法很多,但是代码(或属
性设置)应使用 SQL 语句而不是简单地引用表的名称。例如,可以在 SQL 语句中使用
WHERE 子句确定所提取行的范围。如果用户希望搜寻电话号码簿中所有的牙医,他们会
首先搜寻本地目录(整个地区也许不只一个),并由此开始搜寻。下面提供了将搜寻集
中在三个假想市镇的 WHERE 子句的代码:
SELECT DrName, Phone FROM MyTable
WHERE City IN ('My Town', 'Nearby Town', 'Closeby')

如果觉得对于代码来说查询的范围仍然不集中,请尝试请求用户提供更多的细节信息,
例如在上例中,请求提供电话交换局的名称或牙医的姓氏。
当置入选择框或格时,设计应将选择限制在几百行或更少;因为用户处理大的结果集时
常常遇到困难。除了那些不被其它用户变更,也不随时间变更的静态数据外,客户机复
制的数据从其到达时起就逐渐丧失准确性和相关性。如果用户需要在超过指定限制的列
表中浏览或选择,则请提示集中参数。如果这不可能,那么应考虑使用服务器端游标或
需要内存较少的客户机游标。
详细信息   关于使用缓冲结果集合的详细信息,请参阅本章中“客户/服务器游标结果
集的管理”一节。关于动态游标的详细信息,请参阅“使用 ODBC API”中“ODBC API
支持的游标类型”一节和“使用远程数据对象和 RemoteData 控件”中“创建RDO 游
标”一节。

★ 客户/服务器游标库的选择   介绍本书描述的数据访问模型的游标选项。
数据库引擎使用多种方法创建游标。在某些情况下,引擎创建一组关键字值,它们为基
本表中的数据提供行访问功能。这些关键字值可使用本地 RAM 和临时 (TEMP) 磁盘空
间在客户机中创建,或者使用 TempDB 空间(使用 Microsoft SQL Server 6.0 版时)
在远程服务器创建。当游标包含过多行时,这个空间可能被耗尽。无论游标在哪儿创
建,以大型表的整个内容创建游标都是不可取的。
有多种游标库供客户/服务器前端应用程序使用。如下表所示,每种库都支持若干类型
的游标:
游标库    支持的远程游标类型

DAO ODBC通过Jet  动态集类型(键集)、快照类型(静态)、仅向前的记录集
DAO ODBCDirect  动态、动态集类型(键集)、快照类型(静态)、仅向前的记录集
RDO(ODBC 客户端) 静态、仅向前的结果集
RDO(服务器端)  键集、静态、动态、仅向前的结果集
RDO (客户批)   键集、静态、动态、仅向前的结果集
ODBC API   键集、静态、动态、仅向前
VBSQL    键集、静态、动态、仅向前、'n' 行缓冲
服务器端游标的使用
在装有 Microsoft SQL Server 6.0 版或其它远程引擎 SQL Server 的服务器上可以创
建游标。这意味不需要在网络中传输游标键集,也不需要由客户机管理游标键集。这也
意味由游标选定的行数据只在请求时才发送给客户。与常规的客户端游标相比,服务器
端游标可能会使性能显著提高,特别是考虑到网络的通信量或客户工作站的性能时。然
而,如果潜在的用户数和游标的大小可能耗尽服务器的资源,那么服务器端的游标常常
不是可行的解决办法。
SQL Server 服务器端游标与客户端游标的一个显著的不同是前者由 SQL Server 负担
游标键集的存储,即键集存储在 TempDB 数据库中。正因如此,可能需要扩充 TempDB
以适应游标键集。虽然利用服务器的 RAM 扩充 TempDB 可以提高性能,但是如果游标
过大或过多的用户打开游标,则很容易耗尽 TempDB 的资源。无论如何,把游标限制在
几百行内是有意义的。
服务器端的游标还允许连接的多次操作。这就是说,游标一旦创建,就可以使用相同的
连接变更多个行──无需建立附加连接处理基本的更新查询。
SQL Server 6 服务器端游标可以用下面的方法请求创建:通过 ODBC 或 VBSQL 使用
API 函数,通过事务 SQL 命令,通过 ODBCDirect,或者通过使用 RDO 或 RemoteData
控件。由于性能的原因,这里只推荐 ODBCDirect、RDO、ODBC以及 VBSQL 方法。
请注意,如果查询返回多于一个的结果集,则服务器端游标不支持其执行。然而,如果
请求仅向前的、只读的、行集合为 1 的游标,则可以通过服务器端游标库使用这类查
询。这种类型的选项禁止与游标相关的滚动并允许游标驱动程序管理单个的结果集。
客户端游标的使用
通过网络,客户端游标将键集或实际数据行转移到客户工作站。在把当前行移向结果集
的末尾 (MoveNext) 时,下一个键集的书签将被取来。如果引用数据值,则将根据与行
键集值相关的书签重取。使用 MoveNext 或 MoveLast 方法到达结果集的末尾时,表明
结果集已完全充满,静态和仅向前类型的游标成员也已固定。在某些情况下,对于查询
但没有置入的行,页与行锁定不变。
如果允许静态和键集客户端游标包含过多的行,那么将显著增加工作站的负担。从经验
来看,200 行左右是一个上限。虽然所有的接口都可以创建远远超过 200 行的游标,
但是如果应用程序取大型的行集合,则往往不能很好的扩展,而且性能也很差。
客户批游标的使用
许多应用程序要求一次取大量的行,并且有时需要做包括整个行集合的协调更新。在这
些情况下,为了提高性能或实现更精确的事务管理,允许将实际的数据更新推迟到整个
过程结束后进行。也可以使用客户批游标库创建静态的游标,而后中断它与数据源的连
接。这时可变更行,然后重新连接并以批的形式将变更记录在数据源。
RDO 客户批游标库被设计为处理这类批操作。在使用 rdConcurBatch LockType 选项打
开 rdoResultset 对象时,将创建一个与其它游标相同的游标。然而,当使用AddNew、
Delete 或 Edit 方法时,Update 方法不再将变更写入数据库。这些变更存入由游标库
管理的本地结果集备份,但没有传递给数据库。当使用 BatchUpdate 方法时,所有变
更的行(而且只是那些变更或添加的行)通过一个 ODBC 操作传递给远程数据库。
ClientBatch 游标库也可以由 ODBCDirect 访问。

★ 客户/服务器游标集合管理   概述查询返回结果的管理。
游标一旦创建,可以用它访问被查询请求的任何返回值。通常,这些值以下面三种形式
之一返回:
■ 查询中的 SELECT 语句请求的一个或多个结果集。这些值的返回是通过将当前行指
针指向特定行并引用列(字段)值。
■ 返回状态。存储过程总是返回一个返回状态值,指示过程的成功或失败。
■ 输出参数。某些过程除了返回行集合数据外,还返回附加的参数。
多结果集查询的使用
某些 SQL 语言,例如 Microsoft SQL Server 的事务 SQL (TSQL),可以将多 SELECT
语句组合成批。批可以包括象程序一样的逻辑:更新、删除或插入语句;或包括其它能
够改变所创建的结果集数目和类型的复杂代码。另外,可以创建基于服务器的过程,这
些过程作为数据库的一部分被编译、存储。这些存储过程可以包括能够改变结果数目和
类型的逻辑。因为一个保存的逻辑也可以调用其它保存的逻辑,所以应用程序要正确地
传递结果集。
如果没有过程逻辑的详细情况,就无法断定它到底生成了多少结果集。代码应能识别并
处理那些返回 n 个大小不定、复杂程度不一的结果集。
所有的 Visual Basic 数据访问程序设计模型都支持多结果集的管理──但所用方法差
异很大。从存储过程中提取的数据是静态的而且是不能更新的。如果可以使用 WHERE
子句识别特定的行,则可通过 SQL 动作查询更新单个的行。
查询过程返回参数的管理
存储过程(以除数据行结果集形式外的任何形式)返回输出参量和参数。例如,可以用
SQL Server 存储过程定义单个返回参量和多输出参数。所有的程序设计都通过各种技
术支持输出参数和返回值,然而,许多比较麻烦。
返回值和 OUTPUT 参数的捕获
在执行远程存储过程时,将返回一个综合状态,指示成功或失败。某些情况下,这是唯
一指示过程成功与否的方法。使用 RDO 和 ODBCDirect 时,返回值和 OUTPUT 参量可
以通过一个参数传回 Visual Basic,这个参数的 Direction 属性需设置为
rdParamReturnValue、rdParamOutput 或 rdParamInputOutput。ODBCDirect 也支持类
似的参数方向类型。为了捕获这些参数,查询必需使用 ODBC 调用语法。
详细信息   关于提交参数查询的详细信息,请参阅“使用远程数据对象和 RemoteData
控件”一章中“创建 RDO 参数查询”一节。
使用缓冲的结果集
大多数情况下,在需要时使用缓冲的结果集变更数据比使用可更新的游标与动作查询更
有效率。并不是所有的应用程序都需要使用游标访问和更新数据。实际上,要检索数
据,游标应是最后选择的技术──选择影响最小的游标。例如,只有需要在结果集中实
现前滚或后滚时才使用游标。游标的代码很容易编写,但它使应用程序难以扩展。
不需要关联的键集,一个返回行数据的查询可以生成缓冲的结果集。在下述两种情况下
应当使用缓冲的结果集:应用程序必需访问远程服务器的数据而且需要访问结果集中的
许多行;可以单个访问的结果集的行数受到限制。实现缓冲结果集的方法是:在客户端
创建缓冲区,缓冲区的大小可从一行到客户机能够管理的最多行。因为只需下载结果集
关键字的子集或数据,所以这种方法速度快而且可以控制对客户机 RAM 资源的影响。
VBSQL 和其它接口只能使用向前的结果集,缺省为缓冲结果集。
 五.异步多用户客户/服务器设计问题
大多数客户/服务器应用程序完全为处理多用户而设计──这是客户/服务器设计的本
质。用户可能不是人类操作者,这种情况在创建过程控制系统时会发生,因此,试图调
整应用程序使其能被多个用户同时访问时,有许多设计条件要考虑。
下面的主题讨论一些这样的考虑:
★ 共享客户/服务器数据    如果应用程序在创建游标或执行块更新时没有考虑其它用
户,那么它就可能无法扩大以支持相当数量的用户。
大多数数据访问程序设计模型使用 2K 基于页的锁定方案。通常,这意味着当为更新而
锁定一行(记录)后,整个页和此页上所有其它行在操作期间都被锁定。有时,在更新
或删除操作影响大量行时,DBMS 可能为了加快处理而将页锁定升级为表锁定。使用
SQL Server 6 时,可使用锁定升级选项控制强制升级为表锁定的页锁定数目。在 SQL
Server 6.5 系统中也可指定行锁定。
所有数据页的行都属于相同的表。正因于此,可通过变更数据列分配缓解锁定状况。这
个规范化过程将列信息传播到更多的相关表中。如果表中有一个大小接近 2K 限制的
行,则也许需要提供某种程度的规范化,即通过有效的表设计减少关系数据库中重复的
信息。
注意   如果某行大于 1K,则在 2K 页上这样的行只能放置一行,因为行无法跨越数据
页。
锁定策略
所有的 Visual Basic 数据访问程序设计模型都提供了几种锁定方法。有时,可以通过
如下方法控制锁定的程度:
■ 事务操作 (BEGIN TRANS/COMMIT TRANS)。
■ 一个 HOLDLOCK。
■ SQL SELECT 语句中的其它参数。
 通常,下述方针可帮助定义锁定策略。请务必选择一个能提供足够灵活性的程序设计
接口以实现重要的方针。
■ 避免其它竞争过程试图在相同数据页中执行更新或插入操作。例如,这种情况可能
发生在非索引表,因为所有的添加都应用到链的最后一页。为减少这种情况的发生,请
创建可由各个不同组访问的、分开的历史表。另外,请创建簇索引以在若干页中分配数
据。
■ 避免创建包含用户交互的事务。因为在事务期间锁定将保持,那么一个用户就可能
阻塞整个系统。
■ 保持修改数据的事务尽量的短。事务越长,锁定就越长,这样将阻塞其它活动并可
导致死锁状况的增加。
■ 保持事务在单个批中。不可预知的网络故障可能延迟事务的完成,这将释放锁定。
■ SQL Server 系统只有在需要时才使用重复读入、串行隔离级或带有 HOLDLOCK 的
SELECT。等待锁定释放可能延迟更新。
■ SQL Server 系统中,在创建索引以减少需要相同页的随机更新时,请减少填充因素
(它决定在对现有的数据创建新索引时,SQL Server 设置的每页的充满程度)。对于
那些经常访问的小型表,这一点尤其有用。

★ 实现客户/服务器数据访问应用程序多任务   鼓励在可能时使用异步技术,因为这
样可以预防应用程序死锁,并能更好的利用工作站处理器的空闲时间。
Windows 95 和 Windows NT 融入的最重要功能之一就是对真正的多任务的支持。这意
味着在前台执行的程序继续接受 CPU 周期的同时,其它后台操作可以继续进行。这个
特性并不意味应用程序的表现将有任何不同──只是说明当应用程序执行某些操作的时
候,用户可以继续其它工作。这同样意味着应用程序不应强迫用户长时间等待,不给计
算机系统执行其它工作的机会──甚至是取消当前运行的操作。
本书讨论的程序设计模型,其工作方式可以是同步方式、异步方式、或者两者兼有。在
应用程序执行查询时,数据接口的行为是下面的几种之一:
■ 它将一直阻塞(等待),直到操作完成或至少结果的第一行已经准备好。这是同步
处理的例子。
■ 在查询返回任何结果集之前立即交回控制权,此时数据接口在后台继续取结果集的
第一行。这是异步处理的例子。
■ 在第一行返回后交回控制权并继续在后台将数据置入到结果集直到全部置入。这也
是异步处理的例子。
 如果执行查询要花费许多秒,则应使用某种立即将控制权返回应用程序的异步技术。
这种情况下,应用程序周期性地检查数据接口,直到数据已经准备好。代码也应在循环
中包括 DoEvents 语句,以使应用程序的其它部分(以及其它在 16 位操作系统上的应
用程序)能够继续进行。
重入的管理
异步操作的另一方面是重入。设计重入代码是为了使在同一时刻,程序的多个部分都能
执行代码。例如,如果代码中包括 DoEvents 语句,或者操作系统支持真正的多任务,
那么用户可以执行已经运行的过程。另外,如果在数据访问引擎创建结果集的同时应用
程序可以继续进行处理,则用户可以:
■ 选择取消当前查询的选项。应该编写这样的代码:它允许应用程序取消运行的查
询,并为任何其后的请求准备数据接口。除非正在使用事务,否则应用程序不应试图取
消正在修改数据的查询。
■ 启动另一个查询。这个附加的查询可能干扰第一个查询,也可能只是由于操作员的
急噪而引发的一个重复的查询(或无意的多余鼠标单击的结果)。代码或者应该防止多
操作,或者应该处理多操作。利用某些程序设计模型,可以容易地创建任何附加的连接
或结果集,并启动一个新的查询。如果使用其它的模型或在某些服务器上,在另一操作
启动之前,前一操作必需完成。
■ 处理部分的结果集并请求另一个查询。代码应取消剩余的结果集,并在继续进行前
重置数据接口。如果应用程序支持多结果集,则代码应简单地刷新当前结果集并移向下
一个结果集。

结果集置入的管理
异步操作的一个重要部分是结果集的后置入。因为服务器可能保持对查询中正在检索的
页的锁定,所以最好尽快地完成查询以释放这些锁定。这个问题只存在于非游标(块)
操作中。对于服务器端游标,键集或者在后台被服务器置入,或者没有被置入(动态游
标的情况下)。
并非所有的 Visual Basic 数据访问程序设计模型都支持异步操作的所有功能。常常需
要执行后台结果集置入操作或在代码中轮询的逻辑。
创建同步和异步应用程序
当应用程序长时间没有反应时,用户常常认为它已经崩溃。虽然可能已经显示了一个沙
漏光标来指示处理正在进行,但是如果等待的时间超过 10 秒,用户就可能不耐烦了。
当等待时间接近 1 分钟时,用户可能会重新引导。
应用程序可以通过许多方法防止用户重新引导一个运行中的应用程序。异步操作是一种
有效的技术。ODBCDirect、RDO、VBSQL 和 ODBC API 都提供强健的异步选项,这些选
项允许执行查询并立即重新控制应用程序。每一个这样的模型,代码都周期地轮询接口
以查明操作是否完成。在等待的过程中,应用程序可以显示动画或进度栏,甚至在用户
认为所花时间太长时,可以取消操作。这些技术使用户时刻掌握应用程序的情况,并能
随时控制应用程序。
创建事件驱动应用程序
为消除轮询接口以测试查询是否完成,RDO 程序设计接口第一次提供了全套的事件。异
步和同步查询都可在查询完成或即将执行时激发事件。可以通过为这些事件创建过程来
获取对应用程序更高程度的控制,并防止发生那些导致用户重新引导系统的死锁。
RDO 事件不通过 ODBCDirect 显露。

 六.数据访问选项总结详细介绍一个表,此表比较了这里讨论的各种数据访问程序设
计模型的功能。
《客户/服务器应用程序开发指南》描述了用于 Microsoft SQL Server 6.0 版的数据
访问程序设计模型,表 9.1 比较它们的功能。虽然其中有些比较同样适用于其它远程
服务器系统,但是有些比较对其它驱动程序或服务器并不适用。
表 9.1   Visual Basic 数据访问选项





功能   DAO/Jet   远程数据对象  ODBC Direct  ODBC API VBSQL API

远程连接  自动   自动和人工  自动和人工  人工   人工
连接时间   快速连接选项  3-8 秒   3-8 秒   同左   1-5 秒
(第一次连接) 2-5 秒
附加表3-5秒  快速连接   快速连接
OpenDatabase 选项 2-5 秒  选项 2-5 秒
5-6 秒
异步打开连 否/否   是/是    是/否    是/否   否/否
接/连接的事件
支持远程数据 是   是    是    是   是
库的域管理
安全机制
支持的游标 表类型ISAM, 键集,动态, 静态,   同左    同左   键集,静态,动
类型   Dynaset类型 和仅向前的         态,和 n-行仅
(键集),快照类            向前的
型(静态)和仅
向前的快照类
型Recordset
对象
在用户工作时 是,使用Data 是,使用RemoteData 是,使用Data控件 否   否
的自动后台   控件  控件和异步方式  和异步方式的
游标置入     的RDO   ODBCDirect
异步查询  否(Data控件允 是    是    是   是
许CTRL+
BREAK)
事件通知  否   是    否    否   只有错误和消息
对SQLServer6 否   是    是    是   是
服务器端游标
的支持
无需游标的 是,通过仅向前 是,通过仅向前的 是,通过仅向前的 是,通过SQLEx- 是
行访问  的快照类型 rdoResultset对象 Recordset对象或 tendedFetch,
Recordset对象   或rdUseNone  dbUseNoCursor SQLFetch和
仅向前的游标
结果集的高 是   是    是    是   是
速缓存
执行存储过程 是,通过   是,通过单个运行 同左    同左   同左
  (SP)  SQLPass-  或批
Through
提供的SP参数 连接的  连接的或通过  连接的或通过  连接的或集成 连接的
rdoParameters  Parameters  到 API
SP 结果  是   是    是    是   是
SP 输出参数 否   是    是    是   是
SP 结果代码 否   是    是    是   是
对批中多个 是,通过  是    是    是   是
SQL 语句  SQLPass-
的支持  Through
错误处理程序 置入Errors集 置入rdoErrors集合 置入Errors集合并 在函数返回, 返
回错误和
合并触发可 并触发可捕获  触发可捕获的错误 SQL_ERROR 错误回调事件
捕获的错误   的错误       后调用  处理程序
           SQLError
           检索错误
错误数目和文 Errors集合  rdoErrors集合  Errors 集合  SQL_SUCCE- 错误回调事
本返回的途径:            SS_WITH_- 件处理程序
INFO
功能   DAO/Jet   远程数据对象  ODBC Direct  ODBC API VBSQL API

在查询超时 否   是(异步方式)  是(异步方式)  是(异步方式) 是
后重试/继续
返回的消息 以LogMessa- 完成操作后检查, 完成操作后检查, 完成操作后检 消息回
调事
ges属性在  并与错误一起返回 并与错误一起返回 查,并与错误 件处理程序
.mdb 表中注         一起返回
册的消息
TSQLRais- 停止查询  函数返回错误; 通 同左    函数返回错误; 触发错误和
Error 对接口    过 rdoErrors集合     通过SQLError 消息处理程序
的影响     返回错误和消息      返回错误和消息
支持图形数据 通过绑定控件 同左    同左    可编程的  可编程的
的复杂数据类 的自动方式,
型处理和接口 可编程
绑定控件的 是   是    是    否   否
访问
DBGrid置入 自动   自动和可编程的  自动和可编程的  可编程的  可编程的
ISAM  是   是    是    是   否
置入的范围
多个数据库的 是   否    否    否   否
异种连接

第二节 使用远程数据库数据访问对象

 使用远程数据库数据访问对象 现在,数据访问对象 (DAO) 既可用于独立方式的
Microsoft Jet 数据库引擎,也可以使用新的 ODBCDirect 选项直接访问远程数据源。
这部分讨论 DAO 在两种模式下的功能,其中强调了针对大型客户/服务器应用程序而进
行设计的考虑。首先,数据访问对象 (DAO) 既可以和 Microsoft Jet 数据库引擎一起
使用,也可以通过设置 ODBCDirect 选项而不和它一起使用。本章主要讨论利用数据访
问对象 (DAO) 访问远程数据库时出现的设计和实现问题。
这里讨论的许多问题在《联机手册》中《数据访问对象指南》和 Microsoft Data
Access Objects (DAO) 两部分讨论得更为深入,Jet Database Engine Programmer's
Guide 和 Hitchhiker 的 Hitchhiker's Guide to Visual Basic and SQL Server
(这两本书均由 Microsoft 出版社出版)两本书也包含有关于使用 DAO 访问使用 Jet
的 ODBC 数据源的许多有用信息。
主题

 一.使用 Jet 的 DAO 远程数据访问对于通过 DAO 使用 Jet 引擎进行的远程数据访
问作了简要的介绍。本节讨论 DAO 与 Jet 引擎连接时的功能。Microsoft Jet 数据库
引擎是一个独立的数据库管理系统,需要时它既可以对远程各种服务器进行处理查询,
又可以进行路由查询。通过 DAO 访问 Jet 使 Microsoft Visual Basic 开发工作更加
简便,因为它提供了面向对象的开发方法和对数据识别绑定控件的访问能力。
通过使用 Data 控件、DAO 或者 Microsoft Access,可以创建与数据库无关的虚拟代
码,因为 Jet 可以自动完成所有的语法和数据转换操作。例如,不需引用远程服务器
的功能,就能编写访问不同种类数据源的应用程序。这些数据源可以是开放式数据库连
接 (ODBC) 的各种数据库,如 Microsoft SQL Server,也可以是索引顺序访问方法的
(ISAM) 数据库,如 Microsoft FoxPro、Paradox 或 dBASE;还可以是另外的 Jet 数
据库。
与其它大多数独立数据库引擎所不同的是,Jet 能在几种不同的数据库之间执行异构连
结。如果要想使用 ISAM 格式存储的部门数据,并且需要将它与一个中心服务器上的数
据合并时,那么这一功能就很重要。

 二.DAO 的 ODBC 用户管理简要总结了诸如容量、安全和维护之类的一些问题。数据
库应用程序的每个实例均会用到锁定在服务器上一定数量的连接和数据页(或行),并
对网络造成相当的负载。由于每个额外用户都要用到许多相同的资源,所以系统能够支
持的用户数直接与每个应用程序实例所需的资源数成比例。
为了减少锁定的数目,不应允许用户“搁置”在非置入的 Recordset 对象上。应用程
序应该通过使用 DAO、Data 控件或某种后台置入技巧尽快置入记录集。
设计中应该包含用户登录 ID 和密码的管理。如果设计用到了共享的 Jet (.mdb) 数据
库,则还应该登入 Jet 安全系统。
由于周期性维护,所有用户都必须与 Jet 数据库(它包含有数据)脱离连接,所以应
该采用适当的途径通知用户与共享的 Jet 数据库脱离连接,或是给应用程序发送信号
使之自动脱离连接。如果维护操作不在高峰期进行,而且应用程序在一定空闲时间之后
能自动与 Jet 数据库脱离连接,那么维护程序可执行,它不会干扰未完成的结果集或
使更新挂起。
详细信息   请参阅本章后面的“DAO 的 ODBC 与 Jet 连接的管理”。

 三.DAO 的网络流量管理列出了着眼于网络容量的设计和调试问题. 通过 Jet 引擎使
用 DAO 时,首要考虑的是网络传送的数据量。尤其是当设计中用到了一个包含有本地
的、非附属数据的共享 Jet 数据库时,情况就更是如此。在这种情形下,通过多用户
竞争共享的数据页,网络将承担所有磁盘 I/O 的通信。在大多数情况下,如果共享数
据库中只包含有一个或多个的附属表,那么只有查询结果才需要在网络上传输。
通过合理地使用 Recordset 对象的大小和选取查询处理器,应用程序可以间接地来控
制网络流量。在很多情况下,Jet 查询处理器可以在创建 Recordset 对象时,只产生
相对较小的网络流量。可是有些设计却不能满足其用途,于是就会产生比其它编程模式
更大的网络流量。通过合理调整传给 Jet 查询处理器的 SQL 查询,可以更好地改善它
的功能,同时还可以改善网络的性能。
通过 Jet 引擎访问附属表时,只有链接信息和查询结果才需要在网络上传输。但是如
果要求查询处理器下载远程表的一部分或全部,那么网络流量就会急剧增加。
在广域网 (WAN) 上使用 Jet 数据库是可能的,并有严密的错误管理,WAN 应用程序的
实现具有一定的安全性。但是设计必须有其它预防措施,它必须具有非常强健的错误管
理,能够预料到网络在访问远程服务器时,可能出现的数据损失和很长的响应时间。由
于 WAN 的速度要比常规的局域网慢得多,所以一定要特别注意所产生的网络流量和
DAO 超时的数值。为了更好地了解对远程 ODBC 服务器进行查询的数量和复杂性,应该
经常检查 SQL 跟踪日志。不管网络的拓扑结构如何,对所有网络操作都要使用更加强
健的错误处理,这是良好的设计习惯。
使跟踪日志有效
ODBC 驱动程序管理器能够将所有 ODBC 操作都记录到一个外部文件中,这是能够利用
的最有用的调试和调整工具之一。通过 Windows 控制面板中 ODBC 管理对话框中的相
关选项,可以允许文件使用 ODBCDirect LogMessages 属性。值得注意的是在将应用程
序作成产品之前一定要关闭记录,因为记录过程会严重影响产品的性能。
另外一种可供 Microsoft SQL Server 开发人员选用的跟踪工具是新开发的 SQLTrace
工具,它使得开发人员能够交互地查看所有应用程序传送到 SQL 服务器上的查询。一
旦启动了 SQLTrace 工具,就会打开一个窗口,并在其中显示每一个查询或其它操作请
求。
详细信息   请参阅“选取与 Jet 一起使用的 DAO 查询处理器”和“DAO 的 ODBC 与
Jet 连接的管理”。

 四.DAO 的 ODBC 与 Jet 连接的管理列出了打开一个使用 Jet 引擎的 ODBC 数据源
连接时相关的一些问题。当通过 DAO 或 Data 控件使用 Jet 访问 ODBC 数据库时,
Microsoft Jet 数据库引擎及其连接管理例程就被自动调用。Jet 总是试图只打开尽可
能少的连接,在可能的情况下,它总是尽量共享已有的连接。
一般来说,Jet 数据库引擎总是尽量共享应用程序已经建立的连接或是对它作高速缓
存。首要的原则是,如果请求的数据源名称 (DSN) 被多个 OpenDatabase 调用所引
用,那么 DAO 和 Jet 就试图重新使用这个连接。也就是说,如果对某一个特定的 DSN
已经打开了一个连接,只要 Jet 认为可以共享已存在的连接,它就不会再打开另外一
个连接。当对一个 DAO Database 对象使用 Close 方法时,如果某个连接是特定 DSN
的最后一个连接,那么 Jet 并不会马上关闭它— 而是对它进行高速缓存以防可能重新
建立这个连接。
连接的打开
使用 DAO 和 Jet 来访问远程 ODBC 数据源有两种基本的方式。两种方式都要对以下数
据源调用 OpenDatabase 方法:
■ ODBC 数据源。这种情况下,OpenDatabase 方法的连接字符串参数规定了一个指向
ODBC 数据源的 DSN。
■ 包含有连接的(附属的)ODBC 数据源表或视图的 Jet 数据库。这种情况下,
OpenDatabase dbName 参数指向一个 Jet .mdb 数据库。Jet 数据库包含有 DSN 和用
户的名称及密码,后者是可选项。
  每一种技术都有如《数据访问对象指南》中的“访问外部数据”中的“链接外部表”
和“打开外部表”所描述的优点和缺点。 但是对 ODBC DSN 使用 OpenDatabase 方法
时,要求 Jet 下载较大量的关于目标数据库的、类似于数据定义语言 (DDL) 的描述信
息。由于这种信息没有进行高速缓存,所以每当打开 Database 对象时,DDL 均会重复
读取操作。这是 Jet 尽量将连接存放在高速缓存区的原因之一─ 不必重复这一过程。
将一个 ODBC 数据源附属或链接到一个表时,总是要对 Jet 数据库中的 DDL 信息作高
速缓存,所以如果连接已经建立或者数据库已经打开,就用不着进行 DDL 查询。这在
打开连接或创建 DAO Database 对象时可以极大地提高性能。
详细信息   关于连接策略的详细信息,请参阅《数据访问对象指南》中的“开发客户/
服务器应用程序”和本章后面的“对 DAO ODBC 与 Jet连接的高速缓存”。
(一) DAO数据输入源名称输入项的管理
一般来说,ODBC 连接总是需要 DAO 数据源名称 (DSN) 输入项。根据操作系统的不
同,这些输入项被保存在 ODBC.ini 文件(16 位系统)或者是系统注册表(32 位系
统)中。不应试图自行改变这些输入项。相反,应使用 Windows 控制面板的 ODBC 管
理 applet 或者是 RegisterDatabase 方法来改变它们。
注意   当使用 DAO/Jet 模式、ODBCDirect、远程数据对象或 ODBC API 时,只要连接
字符串中提供了有关远程服务器的足够信息,常常不必去创建或引用已注册的 DSN。
缺省数据库的设置
必须保证代码在连接过程中进行正确的缺省数据库设置。可以规定哪些用户 ID 不能访
问其应用程序将要使用的数据库,或者让他们使用不同的缺省数据库。缺省数据库可以
通过以下途径进行设置:
■ 在 DSN 输入项中包含缺省数据库名。
■ 在连接字符串中包含 DATABASE= 参数。
■ 在服务器上建立基于用户名的缺省数据库。
■ 一旦连接已经打开,就提交一个改变缺省数据库的动作查询。
 详细信息    请参阅《数据访问对象指南》中的“访问外部数据”。
(二) DAO ODBC与Jet连接的建立
为了从远程数据源读取数据,Jet 至少需要一个连接。如果还希望对结果集进行更新,
除非还存在另外一个可供使用的连接,否则 Jet 就会试图打开另外一个连接。也就是
说,一个连接用来置入结果集,另外一个连接用来更新它。但是当使用 MoveLast 方法
时,一旦结果集已经全部置入,那么就可以关闭第一个连接,或是对它作高速缓存。
用户 ID 和密码的提供
为了能够访问远程数据源,一般必须同时提供用户 ID 和密码。这些参数可以在 Data
控件的 Connect 属性中给出,也可以在 OpenDatabase 方法中提供。如果没有提供这
些参数,那么 ODBC 驱动程序管理器就会通过一个对话框来收集用户名称、密码和其它
需要建立连接的丢失的信息。使用 DAO 和 Jet 无法来禁止这个对话框。但是通过利用
ODBCDirect 或 RDO 提示参数,不仅可以使该对话无效,而且代码还可获得可捕获的错
误。
直接打开连接
Jet 在管理与 Microsoft SQL Server 的连接上遵循以下原则:
■ 当使用 OpenDatabase 方法时,Jet 将打开一个新的连接,如果高速缓存区中已经
存在了可识别的 DSN,它就会尽量使用已经存在的连接。即使这个 Database 对象被关
闭了,连接也还保持打开(以防以后使用),除非服务器在高速缓存区中能够找到可用
的连接。每一个 DSN 在高速缓存区中只能有一个连接保持在打开状态。
■ 使用 OpenDatabase 方法直接打开一个连接时,DAO/Jet 模式将查询整个数据库以
明确其中每一个可用表的名称。只要 Database 对象存在实例,这些信息就会在
Database 对象中被高速缓冲并保存(不一定要重新读取)。
■ 通过 OpenRecordset,Jet 尽量共享已经存在的连接,或者重新使用高速缓存的连
接,如果这两种办法都失败了,那么就打开服务器的一个新连接,并执行基于 source
参数(或 QueryDef 对象的 SQL 属性)的查询。MoveNext 方法一执行,Jet 就读取前
100 行信息。如果这还不能结束查询,那么就会打开另外一个连接以支持更新。为了支
持更新,第一个连接将一直保留直到记录集全部置入或者关闭。
间接打开连接
使用 Jet 打开一个远程数据源的连接,效率往往更高。通过打开一个包含有与远程数
据库表或视图的 Jet 数据库,可以很简单地实现它。访问这些附属的(链接的)对象
时,Jet 会利用创建这种附属关系时所提供的已高速缓存的连接信息来建立连接。如果
Jet 由于某种原因未能打开连接,ODBC 驱动程序管理器就会通过一系列对话框来试图
收集和登录 DSN 信息以建立这个连接。通过 Jet 使用 DAO 无法使这些对话框失效。
另一种可供选择的途径是通过在一个打开的 DAO/Jet Database 对象上设置 Connect
属性,为 DAO 和 Jet 提供所需的连接信息。使用这种技术,就能够象连接已直接打开
一样来使用 SQL PassThrough 查询。
详细信息   有关说明 OpenDatabase 和 OpenRecordset 的代码实例,请参阅《联机手
册》中的《语言参考》中的“OpenDatabase”和“OpenRecordset”部分。
(三) 对DAO ODBC与Jet的连接的高速缓存
连接管理的关键内容之一,就是 Jet 根据服务器的不同对一个或两个连接进行高速缓
存。对于如 Oracle 类的服务器,由于它允许在连接上挂起结果,Jet 只需对一个连接
进行高速缓存。对于如 Microsoft SQL Server,由于它不允许在连接上挂起结果,Jet
就必须对两个连接进行高速缓存。
注意   由 ODBCDirect 和 RDO 完成的 SQL Server 的服务器游标,就能够在单个连接
上支持多种操作。但是不支持由 Jet 完成的服务器游标。
 当 Jet 需要打开一个连接时,它总是首先检查其内部的连接高速缓存。如果那里存在
一个使用相同 DSN 和数据库参数的连接,并且连接上也没有挂起的未完成查询,Jet
就重新使用它。为了同时进行读/写操作,支持在单个连接上挂起结果的后端数据库系
统,并不需要另外一条连接。
注意   Jet 对用户 ID 和密码连同连接一起要进行高速缓存,所以它并不反复进行提
示。这意味着如果应用程序需要以不同的用户 ID 和密码在服务器上登录,那么就只有
强行关闭某些已经存在的连接操作才可能完成。
 Jet 基于延续的时间和其动作对每一个连接计时。经过一段可配置的连接超时周期
(其缺省值为 10 分钟),Jet 会自动关闭和放下那些处于休眠状态的连接。一个被认
为处于休眠状态的连接,必须没有打开与之相关连的 Database 或 Workspace 对象。
Jet 不会关闭那些仍有未提交的作业或有未读取结果的查询的连接。由于 Jet 会自动
关闭连接,这表明需要时 Jet 也可以自动打开连接。
注意   连接超时的设置可以通过访问 Windows 的系统注册表进行调整。
如果应用程序需要访问一个 Jet 已经超时而被关闭的连接,则这个连接会自动被打
开。连接的重新建立不会对应用程序带来任何问题。
在某些情况下,如果共享的 DSN 是相同的,则在 Jet 等待该 DSN 变为可用时,相对
第二个 Database 对象的查询可能被封锁。
(四) 关闭DAO的ODBC与Jet的连接
关闭 Recordset 或 Database 对象时,或者这些对象已经不在视野之中时,所使用的
连接就会被释放到连接高速缓存中。例如:在某个过程中声明一个 Recordset 对象,
当这个过程结束时,它所声明的 Recordset 对象也会自动关闭,用来支持它的所有连
接也会被释放到高速缓存。
代码访问 Recordset 对象的最后一个记录时,同执行 MoveLast 方法一样,用来置入
记录集的连接被释放到高速缓存。为了对所有打开的 Recordset 对象进行更新或执行
其它动作查询,有一个连接将被保持。
代码关闭 Database 对象或者该对象失去视野,Jet 就会关闭该 Database 对象和所有
与之相关的 Recordset 对象。所有与之相关的连接都被释放到高速缓存。
每个 Data 控件都执行与 OpenRecordset 方法相似的功能。就是说,当初始化时,每
个控件都会创建一个或两个连接(这取决于结果集的大小和将要访问的服务器的功
能)。为了尽快释放连接,Visual Basic 会自动置入由 Data 控件产生的 Recordset
对象。这一过程是在空闲时间中进行的,其执行速率可以在 MSysConf 表中设定。一般
来说,Jet 会维持一个连接以进行更新,但是在每个 Data 控件相关的结果集完全置入
之前,另外一个连接为了返回行必须维持在打开状态。当代码将记录集定位到最后一行
时,就象执行 MoveLast 方法时一样,这个额外的连接就不再需要并被返回到缓冲区
中。

 五.选取与 Jet 一起使用的 DAO 查询处理器简要介绍了使用 Jet 引擎对远程数据库
进行查询有关的一些问题。一旦对数据库引擎进行了初始化并且建立了连接,应用程序
就可以开始从数据库中读取数据。当使用能执行 SQL 查询的远程数据库引擎时,常常
可以选取查询的方式和对象。用来建立游标的查询既可以由客户机的 Jet 查询处理器
来执行,也可以由服务器上的远程数据库查询处理器来执行。有时并没有可选择的余
地,因为查询的种类(或者是其它方面的考虑)限定了只能使用某一种选择。
所使用的查询处理器对于应用程序的性能具有重要的影响。对大多数查询来说,Jet 查
询处理器会分解该查询并将智能的子查询提交给远程服务器去处理。这些子查询所返回
的数据子集可以用来在工作站上完成整个查询。但是,当 Jet 不能进行这种查询管理
时,它就会试着将所有表(或者是那些表中的大部分)送回到工作站以进一步处理。当
这种情况发生时,查询就会用尽全部工作站资源或者是超出查询的超时限定。
强制 Jet 在远程服务器上完成整个查询,可以防止应用程序的瘫痪束缚资源的查询。
但是,在许多情况下,对应用程序查询方式的重新设计对于改善应用程序性能的确是非
常有用的。例如:如果查询从服务器返回的数据有几百行,这时将查询放到服务器上执
行就可能会改善应用程序的性能,但是对于工作站和网络的资源要求仍然是相当重要
的。
详细信息    有关客户机/服务器性能优化和 Jet 查询处理器操作特性的详细信息,请
参阅《数据访问对象指南》中的“开发客户/服务器应用程序”。
(一)  使用DAO来选择Jet查询处理器
所有由 Jet 查询处理器来执行的查询都必须按照 Jet SQL 语法来书写。但是 Jet 的
SQL 语法与服务器使用的 SQL 语法并不总是完全相同的。不管将要访问什么样的数据
库,Jet 的 SQL 语言总是一致的。这一特性极大地提高了代码的可移植性和无缝地访
问异种数据的能力。
按照缺省规定,一旦执行 DAO 查询就调用 Jet 查询处理器。也就是说,除非使用
Execute 或 OpenRecordset 方法的 dbSQLPassThrough 选项,或者创建
SQLPassThrough QueryDef 对象,否则 Jet 查询处理器就会进行语法分析并执行该查
询的 SQL 语法,并要执行在工作站和远程服务器上完成该查询所要求的所有操作。
详细信息   请参阅《数据访问对象指南》中的“关于SQL查询”。

(二)  使用DAO来选择远程查询处理器
有时需要强制用远程查询处理器来执行查询。正如在本章前面的“DAO 的 ODBC 与 Jet
连接的管理”中所讨论的一样,直接打开一个 Database 对象会消耗很多时间和网络通
信量,这是因为远程数据库和它的表结构必须通过向远程数据库发送一定数量的查询来
决定。
在某些情况下,不得不使用远程数据库专用的 SQL 语法,或者是使用远程数据库引擎
的查询处理器,这时就必须利用使用 Execute 或 OpenRecordset 方法的
dbSQLPassThrough 选项来绕过 Jet 查询处理器。也可以创建绕过 Jet 查询处理器的
DAO QueryDef 对象,
为了使用 QueryDef 对象,应用程序将需要访问 Jet 的 .mdb 数据库。有两种
QueryDef 对象:
■ 用于直通查询的 QueryDef 对象,它使用服务器的语法但不能引用附属表。
■ 用于非直通查询的 QueryDef 对象,它使用附属表并尽可能多地向服务器解释和发
送查询。

(三)  使用DAO进行SQL直通查询
在许多应用程序中,既要用到基于附属远程表的 Jet 查询(即由 Jet 数据库引擎来执
行的查询)也要用到 SQL 直通查询。在执行 Jet 查询时,查询引擎决定查询的哪部分
应该送到服务器,哪部分应该在本地处理。这样就将服务器的功能和 Jet 数据库引擎
的功能结合在一起。在一个 DAO Jet SQL 直通查询中,代码提供的 SQL 语句直接由
Jet 送到服务器上,而不需要停下来对查询进行编译。SQL 直通查询一旦完成,如果它
创建了结果集,Jet 记录集处理器就会创建一个快照类型的 Recordset 对象来管理
它。
详细信息   请参阅《数据访问对象指南》中的“开发客户/服务器应用程序”中的“使
用直通查询”。

(四)  DAO查询结果集的大小
Jet 数据库引擎能够从任意大小的数据库中检索数据。但是随着要处理的记录的个数的
增加,必须意识到所需 TEMP 存储空间也相应增加。所有直接打开表的设计,由于不具
有 SQL 查询限制范围的优点,都必须重新设计。当 Jet 建造键集时,如果本地磁盘空
间被耗尽,Jet 就会产生一个可捕获的错误。这实际上并不是 Jet 的限制,而是系统
资源的限制,并且这是应用程序设计低劣的特征。对于需要数据库引擎创建指向每一行
结果集数据物理指针的应用程序,就象创建键集一样,有一个由存储键集的介质容量所
决定的理论上的上限。
在有些游标模式下,键集中仅有一个子集保持在客户机上,或者键集建造在服务器上。
尽管 Jet 记录集支持在不影响客户资源的条件下浏览表类型的记录集,然而当访问远
程 (ODBC) 数据时,就不再支持这种记录集。相反,这种情况下只支持动态集类型和快
照类型的 Recordset 对象。
以上两种记录集均在客户系统上建造键集,必要时可以溢出到磁盘上的 TEMP 空间。快
照类型的数据还下载数据,这可以进一步限制可以建造的记录集的大小。不管在何种情
况下,数据访问策略中都应该包含对结果集范围的限制。这就是说,应该限制查询返回
行的数目。
详细信息   请参阅本章的“选取与 Jet 一起使用的 DAO 查询处理器”和《数据访问
对象指南》

 六.建造使用 Jet 的 DAO 游标列出并描述了 Jet 引擎所支持的游标。
只要所执行查询是返回行,就必须决定创建何种游标来管理结果集。所谓游标其实就是
从数据源返回的满足 SQL 语句所规定的条件的行的集合。SQL 语句按照 WHERE 子句中
条件来决定哪些表和哪些列应该被返回和限制。
在使用 DAO和 Jet 从数据库中读取信息时,必须使用 Recordset 对象来建造一个游
标。这可以通过使用附属表,或者是具有 Connect 字符串和 SQL 查询的 QueryDef 对
象,或者是二者同时使用来完成。
在描述游标时常常会用到以下术语:
■ 滚动表示使用 DAO 的一些方法可以定位到结果集的某一行上。
■ 更新允许表示游标是否允许改变基本数据库中的行。
■ 成员资格是由 SQL 查询在结果集中决定的。当使用 AddNew 或 Delete 方法来增加
或删除行时,或者当数据库有变化时,游标的成员资格可以或不可以反映这些变化。
■ 静态游标中的静态数据值被复制到本地内存中,在那里游标库并不再保持当前数
据。当定位到结果集中某些特定的行时,键集或动态游标就重新查询数据库以便从数据
库中读取数据。

DAO/Jet 模式对于远程数据源支持以下游标:
■ 动态集游标是完全可滚动、可更新的游标,它具有固定的成员资格和动态的数据
值。
■ 快照游标是完全可滚动的只读游标,它具有固定的成员资格和静态的数据值。
■ 仅前向式游标是一种非滚动的只读游标,它具有固定的成员资格和静态的数据值。
 当由 RDO 完成时,ODBCDirect 还能在这个列表中增加一种动态游标。
创建游标常常会给人这种感觉,就是它对工作站的影响最小且能提供最好的性能。使用
Jet 时,由 DAO 创建的游标开销很大,在这种情况下,缺省的动态集类型 Recordset
对象游标是一种可读/写、完全滚动并支持更新的异种连接。选取 DAO Jet 仅前向式游
标,可以极大地改善游标的性能。与之对照的是,缺省的 ODBCDirect 游标是效率最高
的,但提供的功能很少,因为它的缺省设置是只读、仅前向式的记录集。
(一) 使用DAO访问SQL视图
如果远程数据库仅遗弃 SQL 视图,就可以通过将这些视图附属到一个 Jet 数据库上,
并使用 DAO 动作查询对视图创建伪索引,从而访问这些数据。尽管伪索引并不是严格
的索引,但是它允许 Jet 在视图上创建可更新的记录集。如果不准备更新服务器数
据,就没有必要建立伪索引。
SQL 视图也可以由 DAO 通过 ODBCDirect 来访问。有时这些视图可以通过使用远程服
务器上已经可用的索引来更新。

(二) 使用DAO访问存储过程
在某些环境中,访问服务器数据被限制在一些存储过程之中。在这种情况下,所有的数
据请求和更新都通过这些存储过程来执行─尤其是当没有直接访问远程表时更是如此。
在这种情况下,只能使用 SQL 直通查询,如果选取使用 DAO 时,也可以使用
ODBCDirect。如果服务器要求所有查询和更新均通过存储过程来执行,就必须使用 SQL
直通查询来执行 UPDATE 存储过程和 SELECT 存储过程。然后就可以将其它 Jet
QueryDef 对象建立在这些查询的基础之上,就象它们是附属表一样。
详细信息   请参阅《数据访问对象指南》中的“开发客户/服务器应用程序”中的“使
用存储过程”。

 七.使用 DAO 共享远程数据简要介绍了用户和应用程序之间共享数据库有关的一些问
题。
任何客户/服务器应用程序,最初设计的考虑就是如何最好地共享数据资源。无论是使
用 Jet 还是使用远程引擎查询处理器,在应用程序的设计中,都应该包含一些代码,
以处理由它和另外一个应用程序试图访问同一个数据库而造成的冲突,在所有的设计中
都应该包含强健的错误处理,以解决由多个应用程序竞争同一个服务器而造成的意外。
如果要设计包含有附属表集中共享的 Jet 数据库调用,那么每一个客户系统也都应该
包含与数据库兼容的 DSN。由于 DSN 保持在客户系统上,并且只被共享的数据库附属
表用名称引用,所以安装例程就必须保证客户机的 DSN 描述是准确无误的— 并且一直
保持一致— 以消除出现参数改变的可能。
尽管远程数据库引擎应该负责管理它自己的页面锁和资源,但是包括使用 Jet 查询处
理器应用程序在内的所有应用程序,仍然能够无限期地锁定服务器上的页面。

 八.远程 DAO 消息和错误的处理列出了在 DAO 远程数据库访问期间错误处理时涉及
的问题。
 远程服务器系统会产生它自己的错误和消息。数据库本身也可以包含产生用户定义消
息和错误的过程。DAO 和 Jet 数据库引擎一旦收到出错信号,不管什么原因,都会中
断引起错误的查询。对于那些使用 SQL Server 的 RaisError 函数指示警告信息的数
据库,这也许会引发问题。
使用 QueryDef 对象执行 SQL 直通查询时,从其它 ODBC 和远程服务器收到的消息是
可捕获的。例如,SQL Server 的 SQL PRINT 语句产生的消息就能被用户代码捕获。为
了允许捕获消息,代码必须为某个特定的 QueryDef 对象创建一个名为 "LogMessages"
的属性,并将这一属性设置为 True。一旦设置好,所选 QueryDef 对象所产生的消息
就被记录在一个 Jet 表中。
Jet 或远程查询处理器所执行的每一个 SQL 查询,都能产生一个或者更多的 ODBC 或
者其它远程引擎错误。所有这些错误都被收集在 Errors 集合中,不管是在中断模式下
还是在运行时它都可以被访问。一部分消息还有联机帮助,尤其是那些映射到 Jet 错
误号的消息更是如此。绝大部分 ODBC 操作都会产生类属 ODBC 可捕获的错误,它在
Errors 集合其它成员的消息中被解释得更为详细。
详细信息  请参阅《联机手册》的《语言参考》中的“错误集合”和“LogMessages 属
性”。
 九.使用 DAO 和 ODBCDirect 的远程数据访问简要介绍了将 DAO 映射到远程数据对
象的 ODBCDirect 选项的使用。
 Visual Basic 5.0 提供一项新增加的选项:ODBCDirect,它可以用来和 DAO 一起访
问远程数据库引擎。DAO 的这一选项允许应用程序选取 DAO 使用的数据库引擎和接
口。有两种基本的选择:
■ Microsoft Jet 数据库引擎。按照缺省规定,DAO 使用 Jet 执行所有的数据访问操
作。
■ ODBCDirect。当这一选项有效时,DAO 加载远程 Database 对象 (RDO) 库 2.0 并
将所有的数据访问操作委派给 ODBC 数据源。
 一般地,ODBCDirect 将每一个数据访问对象映射到一个等价的远程数据对象。当
ODBCDirect 只是完成了 DAO 的一部分功能时,这种方法允许在访问远程数据库系统
时,使用熟悉的对象模式来影响基于 DAO 的应用程序。
详细信息   ODBCDirect 文档在《数据访问对象指南》一书中。关于 ODBCDirect 和
DAO 的关系在“使用远程数据对象和远程 Data 控件”中也有一些讨论。

第三节 使用远程数据对象和远程数据控件

 使用远程数据对象和远程数据控件 远程数据对象 (RDO) 提供了直接访问 ODBC 数据
源的 Visual Basic 接口,而远程数据控件 (RDC) 则用一个简单的用户界面控件提供
了这些功能。本章详细介绍了 RDO/RDC 在客户/服务器应用程序中的特性和功能。
本章概述性的介绍了远程数据对象 (RDO) 编程模式和 RemoteData 控件的特点。在选
择客户/服务器应用程序的数据访问编程模式时,可以参考本章提供的信息。关于选择
编程模式的详细信息,请参阅“理解远程数据访问选项”。
远程数据对象提供了一系列的对象,用来满足远程数据访问的特殊要求。在 ODBC API
和驱动程序管理器之上,RDO 实现了很薄的一个代码层,用来建立连接、创建结果集和
游标,并且使用尽可能少的工作站资源执行复杂的过程。如果代码创建了 ODBCDirect
Workspace 对象,那么 RDO 也是被 DAO 访问的,关于该实现的文档,请查阅特定的
ODBCDirect 主题。
注意    RDO 和 RemoteData 控件仅能在 32 位的操作系统中使用,如 Microsoft
Windows 95 或 Microsoft Windows NT 等。
主题

一. 远程数据对象的功能介绍 RDO 的主要功能。
利用 RDO 和 RemoteData 控件,应用程序不需使用本地的查询处理程序即可访问 ODBC
数据源。这意味着,在访问远程数据库引擎时,可以获得更好的性能与更大的灵活性。
通过使用 RDO,可以:
■ 创建简单的无游标结果集,或更复杂的游标。
■ 执行查询并处理任意数量的结果集。
■ 执行返回结果集的存储过程,无论存储过程是否带有输出参数和返回值。
■ 执行包括数据操作或数据定义运算在内的动作查询。
■ 限制返回或处理的数据行数。
■ 在不妨碍执行查询的情况下,监视远程数据资源产生的所有信息和错误。
■ 支持同步、异步或事件驱动的异步处理,因此,即使在执行冗长的查询或者重定位
当前行指针时,应用程序也不会被阻塞。
关于 UserConnection 设计器
UserConnection 设计器使用新的 Microsoft Visual Basic 类设计器体系结构,支持
设计时对数据的编程访问。它允许在设计时创建连接和查询对象(基于远程数据对象
的)。这些连接和查询将作为工程级的对象保存。可以预先设置属性,定义新属性和方
法,以及为对象编写捕获事件的代码。
这就提供了一种简化的方法,用于响应由连接和查询引起的事件,这种处理方式类似于
在运行时调用存储过程和客户端定义的查询。
详细信息   请参阅“客户/服务器工具”中的“UserConnection 设计器”。
(一) RDO配置要求
尽管使用 RDO 和 RemoteData 控件可以访问任意的 ODBC 数据源,但是,RDO 的设计
目的是用于智能的数据库服务器,例如 Microsoft SQL Server 和 Oracle 等,它们使
用了很复杂的查询引擎。如果将其连接到不完全遵循 ODBC Level II 的 ODBC 驱动程
序,或者比较简单的数据源,那么许多的 RDO 功能将是不能使用的。
同 RDO 1.0 相比,在与 ODBC 保持一致性方面,RDO 2.0 已经放宽了许多要求。特别
是,RDO 2.0 现在只需要 ODBC 驱动程序支持一种 Level II 一致性操作,该操作支持
SQLNumParams ODBC API 函数。如果不支持这种 ODBC Level II 函数,则 RDO 将不能
创建 rdoParameters 集合,也不能分析 SQL 语句中的参数标记。但是,如果驱动程序
不支持 SQLProcedureColumns 和 SQLDescribeParam 函数的话,那么在代码中必须提
供 rdoParameters 集合中的每一个参数的方向和数据类型。
尽管有些驱动程序可以用于创建和执行查询,但是,如果该驱动程序不支持
rdoParameters 集合的创建,RDO 将悄无声息地失败,集合没有被创建。结果是:任何
对该集合的引用都会导致可捕获的错误。
如果使用 RDO 访问 Microsoft Jet 引擎数据库,那么 Jet 引擎必须被加载到内存
中,所有的 ODBC 请求将通过 Jet 操作实现。同使用 DAO 相比,这不仅需要更多的
RAM 和 CPU 资源,而且有许多 RDO 函数得不到支持,因为 Jet ODBC 驱动程序的能力
是相当有限的。
RDO 是作为 32 位 ActiveX 接口实现的,因此最终的系统必须运行 Windows 95 或
Windows NT。
另外,只有 Visual Basic 企业版的开发者才能够使用 RDO。尽管编译后的 RDO 程序
可以与 RDO 组件一起被发布到无数的机器上,但这些发布的版本仅仅允许运行时操
作。其它的 Microsoft Office 平台或者不具备运行时模式,或者不具备做 RDO 开发
平台的许可证。不过,ODBCDirect 是得到许可的接口,RDO 可以通过它用于
Microsoft Office 的平台上。
注意   RDO 和 RemoteData 控件是 Visual Basic 企业版的 32 位特性,不能在
Visual Basic 专业版、学习版或 16 位平台上进行代码开发,也不能在其中使用 RDO
对象库和 RemoteData 控件。

(二) RDO和客户/服务器设计目标
RDO 和 RemoteData 控件可以满足客户/服务器方面的一系列需求。利用这些远程数据
访问功能,可以:
■ 获取对于远程 ODBC 数据源的高性能数据访问。快速获得复杂查询结果的能力,是
每个数据访问应用程序追求的目标。RDO 提供的性能等级,只有 ODBC 和 VBSQL API
编程模式才能与之媲美。利用远程数据引擎,RDO 大大减少了响应时间,提高了效率。
■ 管理存储过程的返回码和输入输出参数。输出参数是从 Oracle 存储过程中提取信
息的唯一途径,大量被用于单个查询及许多管理性功能,在很多情况下,如果不分析过
程的返回值,就不能确定存储过程是否完全成功。RDO 通过 rdoParameter 对象来支持
对这些参数的访问。
■ 管理多结果集。通过使用一个查询能够返回几个结果集,因此可以更高效率地利用
查询处理程序和系统资源。通过运行一个查询获取的数据,可以填入到多个数据驱动的
列表框和菜单中,从而提高性能。另外,将行计数查询与 SELECT 查询结合起来,即可
精确地设置滚动条和进度状态条。
■ 在单个批处理中提交多个动作查询。在很多情况下,应用程序可以在一个 SQL 语句
中提交一系列的 INSERT、DELETE 或 UPDATE 操作。这可以提高性能,因为它减少了网
络和远程处理的额外消耗,而且使事务管理更为方便。
■ 限制返回或处理的数据行数。用户选择的行数可能超出了 RDO 的实际处理能力,为
了处理这种情况,RDO 实现了一种查询管理,用来限制从数据源返回的行数。这样,就
可以预计查询的响应时间,更方便地管理用于保存游标键集的工作站及服务器资源。使
用同样的机制,也能够限制由于一次数据修改查询所影响的行数。
■ 利用服务器端的游标。某些服务器,如 Microsoft SQL Server,支持在服务器上创
建的游标键集。在正常情况下,这种类型的游标管理能够显著地提高性能,减少网络负
载以及对工作站资源的需求。
■ 异步执行查询。如果查询需要执行相当长的时间,那么在执行该查询的同时,应该
可以执行其它代码,或者取消该查询。RDO 提供了异步查询选项,可以用它来执行查
询,也可以取消异步查询。RDO 还提供了独一无二的事件驱动异步编程接口,从而避免
了轮询工作方式。异步处理的应用范围被扩展到开放连接和使用 MoveLast 方法的情
况。
■ 在初始超时后继续查询。当查询耗尽了在 QueryTimeout 属性中设置的时间时,RDO
允许查询不被取消),继续等待下一个超时周期。
■ 支持改进的多态性和“独立”对象创建。RDO 支持通过 Dim 语句创建
rdoConnection 和 rdoQuery 对象。这些独立的对象可以和其它对象联合起来执行操
作。例如:可以创建独立于任何 rdoConnection 对象的一个 rdoQuery 对象,并在后
来的某一时刻将该对象与一个打开的连接联合起来。
■ 支持分离的结果集。RDO 允许创建静态的可读写游标,并断开它与远程服务器的连
接。rdoResultset 对象中的数据仍然是可以访问和更改的。一旦将该结果集与另一个
rdoConnection 对象重新联合起来,就可以使用 BatchUpdate 方法把脱机时的更改送
到数据库中。
■ 创建并管理开放式批处理更新。尽管 ODBC 游标库支持开放式更新的概念,但它仍
是基于逐行执行的,而不是批处理。这种更新操作会消耗可观的网络和服务器带宽。如
果使用 RDO 新的客户批游标库,则许多行将成组地被插入、更新或删除。这样就减少
了与服务器通讯的次数。从而提高了更新的效率,并减少了网络负载。
■ 使用存储过程更加简单。RDO 允许将参数化的查询以及存储过程表示为
rdoConnection 父对象的方法。传递参数的方式与传递 Visual Basic 函数参数相同,
不必操作任何 rdoParameter 对象。
■ 使用底层的 ODBC 句柄。如果对象模型仍不能提供所需的灵活性或控制能力,那么
只好直接访问数据源。RDO 提供了访问 ODBC 环境、连接及语句的句柄。
■ 减少内存占用空间。很多情况下,客户/服务器前后端应用程序系统受到 RAM 容量
的限制。因此,应用程序设计要节约 RAM 及其它工作站资源,这是很重要的。RDO 的
内存占用数量明显少于其它编程模式,且它不需要为最低级的游标使用本地内存或磁盘
空间。
 二.使用远程数据对象编程介绍 RDO 对象模型。
RDO 对象和集合提供了使用代码创建并控制远程 ODBC 数据库系统部件的框架。对象和
集合的属性描述了数据库部件的特征,也描述了用来操纵它们的方法。在此总体框架
下,可以在对象和集合之间建立联系,这些联系表示了数据库系统的逻辑结构。图
11.1 RDO 2.0 对象模型





















除了 rdoEngine 对象外,每个对象都保存在一个相关的集合中。在首次访问并初始化
RDO 时,RDO 自动创建一个 rdoEngine 和缺省的 rdoEnvironments(0) 的实例。
远程数据对象编程模式与数据访问对象 (DAO) 编程模式在许多方面很类似。但它的重
点集中在处理存储过程及其结果集上,而不是仅用在 ISAM 编程模式的数据访问检索方
法上。下表逐一描述了这些对象。
RDO对象    描述

rdoEngine    基本对象。在应用程序第一次访问 RDO 时,自动创建。
rdoError     用于处理 RDO 所产生的所有的 ODBC 错误和消息,自动被创建。
rdoEnvironment 为特定的用户名定义了连接及事务作用域的一个逻辑集合。包括打开
的和已分配的(但未打开)连接,提供了并发事务的机制,并且还为数据库的数据操作
语言提供了安全的上下文。rdoEnvironments(0) 自动被创建。
rdoConnection 表示远程数据源和该数据源上特定的数据库之间的打开的一个连接,或
者是一个已分配但仍未连接的对象,该对象可用于随后建立一个连接。
rdoTable    表示一个基本表或 SQL 视图的存储定义。
rdoResultset   表示运行一个查询所产生的数据行。
rdoColumn   表示具有公共数据类型和公共属性的一列数据。
rdoQuery    一个 SQL 查询定义,可以包括 0 个或多个参数。
rdoParameter 表示与 rdoQuery 对象关联的一个参数。查询参数可以是输入、输出或
输入输出参数。
 注意   真正的分布式事务仅能在支持 Distributed Transaction Coordinator (DTC)
的数据库管理系统中进行处理。目前只有 Microsoft SQL Server 6.5 支持这种功能。
关于 DTC 的详细信息,请参阅本章的 “使用 rdoEnvironment 对象管理事务”。
 注意   RDO 2.0 支持 RDO 1.0 的 rdoPreparedStatement 对象和
rdoPreparedStatements 集合,但仅仅是为了保持向后兼容。建议将原来的代码改写为
使用 rdoQuery 对象及 rdoQueries 集合的。
创建远程数据对象
使用 RemoteData 控件创建 RDO 对象,正如使用 Data 控件创建 DAO 对象那样。也可
用 RDO 方法创建结果集,并且将其传送到 RemoteData 控件,由相关联的、被绑定的
控件管理和编辑。
远程数据对象也可使用父对象的方法创建,或者用 Dim 语句来声明该对象。如果使用
Dim as New 语句实例化一个新的对象,则该对象不会被追加到相关联的集合。下表表
明了如何创建每一个主要的远程数据对象。
远程数据对象  创建方式

rdoEngine   自动创建。
rdoEnvironment  第一个实例自动被创建。rdoEngine.rdoCreateEnvironment
rdoConnection rdoEnvironment(x).OpenConnection
(也可以由 RemoteData 控件创建) Dim MyCn as New rdoConnection
rdoQuery   rdoConnections(x).CreateQuery
Dim MyQry as New rdoQuery
rdoResultset  rdoQuery(x).OpenResultset
rdoConnection(x).OpenResultset
(也可以由 RemoteData 控件创建)
rdoParameter  为参数查询自动创建。
rdoTable   被引用时自动创建。
rdoError   在返回 ODBC 错误或消息时自动创建。

三. RDO 与 Microsoft Jet/DAO 的对比讨论 RDO 提供了哪些特性,以及它们如何对
应更为流行的 DAO 特性。
使用远程数据对象的方式基本上与使用 Microsoft Jet 数据库引擎数据访问对象
(DAO) 的方法类似,RemoteData 控件也与 Data 控件类似。使用 RDO 可以提交查询、
创建结果集或游标,以及用与数据库无关的、面向对象的代码处理查询结果。
使用 RemoteData 控件,可以在创建的窗体中使用能够被 Data 控件识别的所有绑定控
件;还可以用很少或几乎不用代码处理结果集。
对现有的使用 DAO 及 Data 控件的应用程序作些稍微的改动,即可将其转换成使用
RDO 和 RemoteData 控件。它们之间有一些区别,然而,由于实现和设计了的 RDO 用
于关系数据库,因此 RDO 并没有它自己的查询处理程序;它依靠数据源来处理所有的
查询,并创建结果集。数据对象本身是由 ODBC 驱动程序所返回的结果集和游标来建立
的。
有时可能没有必要将已有的 DAO/Jet 应用程序转换为 RDO,因为 ODBCDirect 将 DAO
通过 RDO,而不是 Jet。如果应用程序没有使用 DAO ISAM 对象及其方法(例如表类型
的 Recordset 对象和 Seek 方法)或其它的 ISAM 编程方法,那么,通过少许改动便
可将其转换成 ODBCDirect 方式,所需的改动甚至比转换成 RDO 还少。
详细信息   有关 ODBCDirect 的详细信息,请参阅本章的“RDO 与 Microsoft Jet
ODBCDirect 的对比”,以及“使用数据访问对象访问远程数据库”中的“使用 DAO 和
ODBCDirect 的远程数据访问”或《数据访问对象指南》的“开发客户/服务器应用程
序”。
 下表列出了 RDO 2.0 对象及其等价的 DAO/Jet 对象:
远程数据对象及其等价的 DAO/Jet 对象

RDO     对象 等价的 DAO/Jet 对象

rdoEngine    DBEngine
rdoError    Error
rdoEnvironment   Workspace
rdoConnection   Database
rdoTable    TableDef
未实现    Index
rdoResultset   Recordset
 未实现    表类型
 键集类型    动态集类型
 静态类型(r/w)   快照类型 (r/o)
 动态类型    (无)
 仅向前类型   仅向前类型
 (无游标的)    (无)
rdoColumn   Field
rdoQuery    QueryDef
rdoParameter   Parameter
未实现    Relation
未实现    Group
未实现    User

远程数据对象使用行和列的概念,而不是记录和域的概念,后者通常为关系数据库的术
语。查询以结果集的形式返回数据,结果集可以包括零个或多个数据行,每行又可包括
一个或多个列。DAO 需要使用游标访问数据,而 RDO 允许创建无游标的结果集,所需
的资源大大少于游标。
有些 DAO 对象、方法和属性被设计用来支持和实现 Jet 的 ISAM 结构以及可安装的
ISAM 数据库。例如,可以使用 Index 对象和 Seek 方法来管理 ISAM 索引,并用索引
进行定位行。因为 RDO 和关系数据库以完全不同的方式管理索引,所以那些对象和方
法都是不需要的。
通过 DAO 方法和属性,DAO 也支持数据库模式的创建、修改、参照完整性 (RI) 以及
安全性。RDO 不支持任何形式的 RI、安全性或模式修改,因为服务器系统所提供的工
具和实用程序完全能够支持这些功能。
还可以运行 RDO 生成表查询,或执行动作查询,用原始 SQL 语句操作创建、修改及删
除数据库或表。也可以执行复杂的存储过程,从而管理数据库模式或执行维护操作,这
对于 RDO 来说是不可能的。
(一) RDO的集合管理
RDO 用集合管理除 RDO 引擎以外的所有 RDO 对象。因为 Visual Basic 5.0 目前支持
创建一些独立对象,例如 rdoConnection 和 rdoQuery 对象,所以不是所有的对象都
会自动追加到它们的父集合上;但在绝大多数情况下,这是自动完成的。独立的
rdoConnection 和 rdoQuery 对象可以用 Add 方法追加到它们的父集合上,用 Remove
方法从集合中删除。
在使用 CreateQuery 方法时,rdoParameters 集合是自动创建的。但是,如果由于某
种原因 ODBC 接口不能对该 SQL 语法进行分析,这个用于管理查询参数的集合将不被
创建。因此,除非 RDO 成功地将 SQL 参数查询作为 rdoQuery 对象的 SQL 属性进行
了语法分析,否则任何对 rdoParameters 集合的引用都会导致可捕获的错误。如果
ODBC 数据源驱动程序不支持 SQLNumParams 函数或该函数返回错误,这个集合也不能
创建。
下表列出了 RDO 管理的集合:
RDO集合    描述

rdoErrors   包括所有的存储 rdoError 对象,这些对象涉及到远程数据对象 (RDO)
的一次简单操作。
rdoEnvironments 包括 rdoEngine 对象的所有活动 rdoEnvironment 对象。
rdoEnvironments(0) 是自动创建的。
rdoConnections  包括在 rdoEnvironment 对象中打开或创建的所有对象,或者利用
Add 方法分配并追加到 rdoConnections 集合的对象。
rdoTables   包括数据库中保存的所有 rdoTable 对象。
rdoResultsets  包括 rdoConnection 中打开的所有 rdoResultset 对象。
rdoColumns  包括一个 rdoResultset 或 rdoTable 对象的所有 rdoColumn 对象。
rdoQueries  包括已追加到 rdoQueries 集合的 rdoQuery 对象,这些对象可能是通过
CreateQuery 自动追加的,或者通过 Add 方法追加的。
rdoParameters  如果 SQL 语句被正确进行语法分析,那么该对象包括了一个
rdoQuery 对象的所有 rdoParameter 对象。它还包括与查询中的每一个标记参数相对
应的 rdoParameter 对象。

RDO 1.0 集合
RDO 1.0 对象和集合的管理方式与 RDO 2.0 不同。当打开或创建一个新的 RDO 1.0 对
象时,它会自动添加到与那些对象相关联的集合中,即使设置一个现有的变量到新创建
的对象中也是如此。
例如,下列 RDO 1.0 代码创建了两个独立的 rdoConnection 对象:
Dim Cn as rdoConnection
Set Cn = rdoEnvironments(0).OpenConnection( _
  dsname:="MyDSN", _
  prompt:=rdDriverNoPrompt, _
  connect:="UID=;PWD=;")
Set Cn = rdoEnvironments(0).OpenConnection( _
   dsname:="MyOtherDSN", _
  prompt:=rdDriverNoPrompt, _
  connect:="UID=;PWD=;")
在执行这些代码后,两个 RDO 1.0 rdoConnection 对象均作为 rdoConnections 集合
的成员被追加。请记住,在代码中必须对那些不再需要的对象使用 Close 方法,甚至
是在赋值到同一个变量时,因为它们不会自动关闭。对于 rdoResultset 对象也是如
此。
RDO 2.0 集合
在 RDO 2.0 中,如果将现有的变量设置为一个新创建的对象,那么在用新的 RDO 对象
替换已有的 RDO 对象之前,将事先释放或者关闭原来的对象。这种改进使 RDO 的使用
方式更类似于 DAO 对象。
当上述代码在 Visual Basic version 5.0 中通过 RDO 2.0 执行时,将只有一个
rdoConnection 对象被创建:第一个 rdoConnection 对象被关闭并从 rdoConnections
集合中删除。如果 Visual Basic 4.0 的代码明确地关闭了 RDO 连接和 rdoResultset
对象,则不必关心向后兼容。但是,如果还必须维护这些代码的话,则必须将新的 RDO
对象赋值到新的变量,否则将要丢失原始对象。

(二) RDO与Microsoft ODBCDirect的对比
DAO 支持一个对象接口选项,该接口将 DAO 对象模型与 RDO 相连接。这个新的接口被
称为 ODBCDirect,一旦设置了 DBEngine 对象的 DefaultType 属性,DAO 引用将一一
被对应到等价的 RDO 对象上。如果 ODBCDirect 是有效的,那么 DAO 将不加载
Microsoft Jet 数据库引擎,而是加载 RDO 2.0。
通常,这个接口是为需要与 DAO/Jet 和 DAO/ODBCDirect 应用程序保持一定程度的源
代码兼容性而设计的。例如,如果要求一个应用程序既要能够访问本地 Jet,又要访问
ODBC 数据库,则 ODBCDirect 可以提供类似的编程方案。
注意   尽管用于 ODBCDirect 的对象名与 DAO/Jet 中使用的基本上一致,但是,有些
可以运用到 DAO/Jet 对象的属性和方法却不能为 ODBCDirect Recordset 对象所用。
类似地,ODBCDirect Recordset 对象所显露的有些属性和方法也不能被 DAO/Jet
Recordset 对象使用。而且并非所有的 RDO 2.0 功能 ODBCDirect 都能实现。
 详细信息   有关 ODBCDirect 的详细信息,请参阅“使用数据访问对象访问远程数据
库”中的“使用 DAO 和 ODBCDirect 进行远程数据访问”或者《数据访问对象指南》
中的“开发客户/服务器应用程序”。

四. 初始化 rdoEngine 对象rdoEngine 对象概述,以及如何设定其属性。
rdoEngine 对象表示远程数据源,在第一次引用 RDO 对象或 RemoteData 控件时自动
被创建。作为最高级别的对象,它包括远程数据对象层次中的所有其它对象。
rdoEngine 对象是预定义的,因此不能再创建另外的 rdoEngine 对象,而且,它不属
于任何集合。可以使用 rdoEngine 设置数据源参数,并创建其它 rdoEnvironment 对
象。
虽然 rdoEngine 可以为使用它的多个应用程序所共享,但 rdoEngine 的缺省属性却不
能被共享。应用程序的每一个实例都有自身的缺省值集合,它们不会影响到使用 RDO
和 RemoteData 控件的其它应用程序。
注意   将 RemoteData 控件添加到工具箱,并不能自动设置到 Microsoft 远程数据对
象库的引用。为了在代码中使用 rdoEngine 和远程数据对象,必须先在“引用”对话
框(可以从“工程”菜单中得到)中设置一个到 Microsoft Remote Data Object 2.0
对象库的引用,否则在第一次引用 RDO 对象时会导致编译错误。而且,如果计算机上
安装了 Visual Basic version 4.0 或 Microsoft Office,RDO 1.0 库会显示在“工
程引用”对话框的对象库列表中。虽然在 Visual Basic 5.0 中也能使用该库,但我们
建议不要这样做。
(一) 初始化rdoEngine缺省属性
在创建新的 rdoEnvironment、rdoConnection 或 rdoResultset 对象时,这些新对象
的特性取决于 rdoCreateEnvironment 或 OpenResultset 方法的参数或 rdoEngine 缺
省属性的缺省值。这些属性如下表所示:
属性     含义       缺省值

rdoDefaultCursorDriver 游标库(ODBC、客户端批处理  rdUseIfNeeded(服务器端的
游标)
     、服务器端的,或者无。)
rdoDefaultPassword  用户密码      "" (空串)
rdoDefaultUser   用户 ID      "" (空串)
rdoDefaultErrorThreshold 致命错误严重程度的下限   -1 (失效)
rdoDefaultLoginTimeout 放弃连接试图之前的等待时间。  15 秒
 在创建新的 rdoEnvironment、rdoConnection 或 rdoResultset 对象之前,可以修改
这些缺省属性值中的任何一个。但是,因为 rdoEnvironments(0) 是在第一次引用 RDO
或 RemoteData 控件时自动被创建的,所以创建它时使用的是上表中所示的缺省值。如
果这些缺省值不适用于具体的应用程序,那么,在创建一个新的 rdoEnvironment 对象
或打开一个连接之前,必须改变 rdoEnvironments(0) 或 rdoEngine 的属性。
捕获 InfoMessage 事件
当 ODBC 接口接收到来自远程服务器的一条信息消息时,rdoEngine 触发 InfoMessage
事件。在 RDO 接收到 SQL_SUCCESS_WITH_INFO 返回码,并将信息消息添加到
rdoErrors 集合之后,将发出该事件。如果 RDO 方法调用产生了许多的信息消息,那
么该事件将只发出一次,即在最后的信息消息添加到 rdoErrors 集合之后。可以捕获
该事件,在检查 rdoErrors 集合的内容之后再决定应该如何处理。
例如,如果提交了一个查询,该查询允许来自 SQL Server 的统计信息或查询计划信
息,那么这些信息将通过 rdoErrors 集合返回,在 InfoMessage 事件发生后,应用程
序将得知这些消息。

(二) 管理RDO错误和消息
每当 ODBC 驱动程序管理器用于执行 RDO 请求时,Visual Basic, ODBC 接口或远程服
务器都可能产生错误。这些错误严重程度不一,有时可能迫使该查询被取消或放弃。
Visual Basic 所产生的所有错误都会触发一个可捕获的错误,可以用 On Error 语句
捕获到它。ODBC 中间件或远程服务器产生的错误都放置在 rdoErrors 集合中。一旦被
RDO 所调用的 ODBC 函数返回了 SQL_ERROR 结果码,Visual Basic 将触发一个可捕获
的错误,表明发生了一个 ODBC 错误。这时可以检查 rdoErrors 集合的每个成员以获
得导致错误的详细信息。
如果 ODBC API 函数的返回码被设为 SQL_SUCCESS_WITH_INFO,则数据源返回的信息消
息将不会触发可捕获的错误。这些消息被放置在 rdoErrors 集合中。
在发生与 ODBC 操作不直接相关的错误时,Visual Basic 也会产生可以捕获的错误。
例如,如果在执行 CreateQuery 方法之后试图设置 rdoParameters 集合,就可能遇到
可以捕获的错误,表明该对象是不存在的。这可能是由于 RDO 无法根据查询的语法建
立一个 rdoParameters 集合而导致的。可以在 On Error 处理程序中检查 Err 和
Error 属性以决定将要进行的操作。
在检查了 rdoErrors 集合之后,就可以使用 Clear 方法清除其内容了。这可以防止在
随后检查 rdoErrors 集合的错误时发生混淆,仅仅发现了那些来自早期操作的残存信
息,这些信息可能与当前的问题毫无关系。
在执行一个查询时,会触发 QueryComplete 事件,无论该查询是否执行成功。可以检
查 ErrorOccurred Boolean 标志以确定查询是否成功,类似地,通过检查 Connected
事件的 ErrorOccurred 标志也可以确定连接操作是否成功。
详细信息   请参阅《联机手册》的《语言参考》中的“rdoError 对象”或
“rdoDefaultErrorThreshold 属性”。

五. 创建 rdoEnvironment 对象rdoEnvironment 对象概述,以及如何将其属性应用到
数据访问问题。
在很多情况下,应用程序不需要创建另外的 rdoEnvironment 对象,因为缺省的
rdoEnvironments(0) 对绝大多数的操作来说都足够了。但是,如果应用程序希望支持
多个事务作用域,或者需要区分不同的用户名及密码上下文,那么必须用
rdoCreateEnvironment 方法创建新的、具有指定用户名和密码值的 rdoEnvironment
对象。该方法只接受不重复的名称、用户名及密码。如果选择的名称与
rdoEnvironments 集合中已存在的成员名称相同的话,将会导致一个可以捕获的错误。
在RemoteData 控件初始化或第一次在代码中引用 RDO 对象时,自动创建缺省的
rdoEnvironments(0)。rdoEnvironments(0) 的 Name 属性是
 "Default_Environment"。rdoEnvironments(0) 的用户名和密码均为零长度字符串
("")。
如果提供了唯一的名称,新创建的 rdoEnvironment 对象会自动追加到
rdoEnvironments 集合中。也可以使用零长度字符串作为 rdoCreateEnvironment 方法
的 name 参数。在这种情况下,新的 rdoEnvironment 不会被追加到 rdoEnvironments
集合。
缓存的登录信息
如果在 OpenConnection 方法的 connect 参数或 RemoteData 控件的 Connect 属性中
没有提供用户名及密码信息的话,rdoEnvironment 的用户名和密码信息将被用于建立
连接。
例如,缺省的用户名 (Fred) 和密码 (Blond) 可以用于在 En environment 中建立连
接:
Dim En As rdoEnvironment
Set En = rdoCreateEnvironment("", "Fred", "Blond")
 一个 rdoEnvironment 对象逻辑上对应于一个 ODBC 环境。通过引用该对象的 hEnv
属性,可以用 ODBC API 函数来访问 rdoEnvironment 对象。但是,因为 ODBC 只允许
每个应用程序对应一个环境句柄,所以 ODBC 环境句柄的存活期在 rdoEngine 的存活
期之内。
(一) 使用rdoEnvironment对象管理事务
rdoEnvironment 对象支持 BeginTrans, CommitTrans 和 RollbackTrans 方法,用于
开始、提交或回滚 ODBC 分布式事务。在支持 DTC 技术的系统上,这些方法使用
Distributed Transaction Coordinator (DTC) 来实现。目前只有 Microsoft SQL
Server 6.5 支持 DTC。rdoEnvironment 对象中的 rdoConnections 集合中的所有连接
都将参与分布式事务。
Microsoft 分布式事务协调器
Microsoft SQL Server 的新特性能够在 Microsoft Windows NT 以及基于 Microsoft
Windows 95 的系统的网络上协调事务,利用 MS DTC,SQL Server 可以:
■ 更新驻留于两个或更多 SQL Server 6.5 系统上的数据。
■ 参与 X/Open DTP XA-compliant 事务处理监视器所控制的事务。这些监视器包括
Transarc 的 Encina,AT&T Global Information Solutions Company 的 Top End 和
Tuxedo 等。
■ 提供一个易于使用的分布式事务图形用户界面。
 也可以从“服务器”菜单中选择“Distributed Transaction Coordinator”命令来使
用 MS DTC。
在网络计算机系统上运行着一系列的分布式软件部件,Microsoft 分布式事务协调器能
够确定发生竞争的事务处理。MS DTC 完全集成在 Microsoft SQL Server 6.5 中,而
且在管理分布式事务的每台计算机上的 SQL Enterprise 管理器中提供一个事务管理
器。
RDO DTC-无关 事务管理
如果没有 DTC,或者当前的 ODBC 驱动程序不能处理分布式事务,这些方法将迫使 RDO
通过 rdoConnections 集合中的所有连接来串行地执行事务。但这些事务不是不可分割
的,也就是说,对于其中一个 rdoConnection 的操作可能执行完了,而对于其它的却
可能没有。在这种情况下,提交的操作不会回滚。因此,它们的成功或失败不是相互依
赖的。
因为 rdoEnvironment 对象决定了应用程序中的事务作用域,所以提交一个
rdoEnvironment 事务也就提交了所有打开的 rdoConnection 数据库(在
rdoEnvironment 对象上,及其相对应的打开的 rdoResultset 对象上打开)上的所有
挂起的事务。这并不意味着两阶段提交。而只是表示一个 rdoConnection 对象被安排
提交所有挂起的事务,每次提交一个。
注意   ODBC 事务模式不支持嵌套的事务。也就是说,在前面的事务被提交或回滚之
前,不能执行第二个 BeginTrans 方法。但是,如果 ODBC 数据源提供这种支持,那么
可以使用 SQL 语句执行嵌套事务。所有的 Microsoft SQL Server 系统都提供该支
持。
 在使用 ODBC 事务时,事务不会跨越连接:在一个连接上开始并被提交的事务并不会
影响其它连接上挂起的事务,即便是相同的服务器和数据库上的两个连接也不会相互影
响。
捕获 rdoEnvironment 事务事件
事务操作一旦完成,rdoEnvironment 对象就会触发一个事件。这些事件可以用于与其
它处理同步事务状态。BeginTrans、CommitTrans 及 RollbackTrans 方法被触发后,
相应的事件也被触发。

(二) 使用rdoEnvironment对象管理连接
可以使用 rdoEnvironment 对象的 OpenConnection 方法来打开与数据源的连接,并创
建 rdoConnection 对象以管理和引用这些连接。在一个 rdoEnvironment 中可以打开
多个连接(每一个访问其自身的数据库)、管理事务,并创建基于用户名和密码的安全
性。例如,可以:
■ 使用 Name , Password 和 UserName 属性创建一个rdoEnvironment 对象,从而建
立一个命名的、有密码保护的环境。该环境会创建一个空间,在这个空间中,可以打开
多个连接并管理多个 ODBC 事务。
■ 使用 OpenConnection 方法在 rdoEnvironment 中建立一个或多个连接。
■ 使用 BeginTrans、CommitTrans 和 RollbackTrans 方法管理一个 rdoEnvironment
中处理的 ODBC 事务。
■ 使用若干 rdoEnvironment 对象来处理多个并发的、独立的、交叉的事务。
■ 使用 Dim X as New rdoConnection 创建一个独立的 rdoConnection 对象,并使用
Add 方法将其追加到 rdoEnvironment 对象的 rdoConnections 集合中。
■ 使用 Remove 方法从父集合 rdoConnections 中删除指定的 rdoConnection 对象。
■ 使用 Close 方法结束环境和连接。
 注意   因为 rdoConnection 对象可以作为独立的对象创建,所以不再总是需要引用
rdoEnvironment 对象。

六. 建立 RDO 连接如何连接到远程数据源。
在引用远程数据库的数据之前,必须先建立到数据源的连接。该数据源可能是远程数据
库服务器,如 SQL Server , Oracle,或者其它具备合适的 ODBC 驱动程序的数据库。
有许多方法可以建立与 RDO 的连接,如以下主题所描述的。但是,与 DAO 不同的是,
RDO 不为应用程序管理连接,它仅能够收集要传递到 SQLDriverConnect 函数的参数,
并调用 SQLDisconnect 函数关闭连接。RDO 不能缓存连接,也不能基于相似的 DSN 项
共享连接。在使用 RDO Close 方法关闭连接时,它会立即被关闭。
如果准备打开一个连接,可以有以下的选择:
■ 使用 RemoteData 控件,根据它的属性建立一个连接,并创建一个 rdoConnection
对象,它的引用保存在 Connection 属性中。
■ 声明一个 rdoConnection 对象,并使用 rdoEnvironment 对象的 OpenConnection
方法。
■ 使用 Dim x As New 语法创建一个独立的 rdoConnection 对象,设置其属性并使用
EstablishConnection 方法。
■ 在创建一个独立的 rdoConnection 对象,或对已有的 rdoConnection 对象使用
Close 方法后,对已存在的 rdoConnection 对象使用 EstablishConnection 方法。
 可以根据特定的编程需求选择上述技术。例如,如果需要提交相同的查询到若干个远
程数据库时,可以创建一个独立的 rdoConnection 对象,通过 ActiveConnection 属
性,能够将多个 rdoQuery 对象指派到该对象。在其它情况下,RemoteData 控件的简
单特性可能是更受欢迎的。
所有这些方法都建立了与数据源之间的物理连接,数据源可以是 SQL Server 或
Oracle 数据库服务器等。要建立一个连接,必须提供数据源的网址、驱动程序类型以
及用于标识用户的可选参数。
一旦建立了连接之后,可以利用它:
■ 使用 OpenResultset 方法执行一个查询,该查询返回一个或多个结果集。
■ 用 Execute 方法执行一个动作查询。
■ 创建一个 rdoQuery 对象,用于执行参数查询或存储过程。
■ 定义一个查询,该查询显示一个或多个存储过程作为 rdoConnection 对象的方法。
■ 通过设置 ActiveConnection 属性,将 rdoConnection 对象与特定的
rdoResultset 对象相联。
■ 使用 Add 方法,将 rdoConnection 对象添加到选定的 rdoConnections 集合。
■ 使用 Remove 方法从 rdoConnections 父集合中删除一个 rdoConnection 对象。
(一) 为RDO提供连接字符串
为了告诉 ODBC 驱动程序管理器要使用什么驱动程序,并告诉选定的驱动程序使用什么
数据源,代码必须以连接字符串或 RemoteData 控件属性设置的形式提供若干参数。在
绝大多数情况下,连接字符串用于将 RDO 指定到特定的服务器、数据库及其用户。
连接字符串中包括一系列的参数,互相之间用分号隔开,这些参数由 ODBC 接口定义,
也包括 ODBC 驱动程序本身。就是说,所有的 ODBC 驱动程序都有特定的参数要求,因
此必须参阅驱动程序所包含的文档以获取特定信息。该连接字符串将和所关联的
rdoEnvironment 对象的 hEnv 一道传递到 ODBC API 的 SQLDriverConnect 函数。
注意  如果要转换已有的 DAO 或 ODBCDirect 代码,必须从连接字符串的开头删除
ODBC 参数。另外 LOGINTIMEOUT 参数是不支持的,必须使用 rdoEnvironment 对象的
LoginTimeout 属性代替它。
通常,连接字符串包括以下参数,但是这些参数不是绝对必需的:
ODBC 连接字符串

ODBC 连接字符串参数 参数的意义

DSN=    已注册的 ODBC 数据源名称。如果使用 DRIVER 关键字,则不用 DSN。
UID=     建立于服务器上的用户名。在 SQL Server 中是登录名。
PWD=    与登录名对应的密码。
DATABASE=   所请求的缺省数据库(任选项)。
SERVER= 数据源服务器的网络名称。在 Microsoft Windows NT 计算机上,"(local)"
可以作为服务器被输入。在这种情况下,可以使用 SQL Server 的本地副本。即便是非
网络版本也可以。
DRIVER= 数据源驱动程序的名称。Microsoft SQL Server 使用 {SQL Server。如果使
用 DSN 关键字,则不用 DRIVER。
APP=    应用程序名(任选的)。
WSID=    工作站 ID。通常,这是应用程序所在的机器的网络名称(任选)。
LANGUAGE=   SQL Server 使用的国家/地区语言(任选)。
注意   以上这参数中有一些是特定于 SQL Server 的,对于 Oracle 或其它 ODBC 数
据源,可能需要其它的必选参数或任选参数。
连接字符串中不需要的参数
注意,连接字符串不包括设置以下选项的参数:
选项      缺省行为

网络地址     不需要,由服务器名称来处理。
网络协议     缺省为 SQL Server 驱动程序中的命名管道。
OEMTOANSI 开关   缺省为 "Off"
使用信任连接选项   缺省为 "Off"
为预备语句产生存储过程  缺省为 "Off"
所有这些选项都属于驱动程序选项,它们都在 Windows 控制面板中的“数据源名称”
对话框中设置。除非使用 DSN 来建立连接,否则必须接受每个参数的缺省值设置。
提供 User ID 和密码值
为了能够访问绝大多数的 ODBC 数据源,必须提供一个有效的用户 ID 以及相应的密
码。这些值最初由系统管理员注册,但应用程序可以修改他们。通过设置
OpenConnection 或 EstablishConnection 方法的适当的提示参数,就可以很简单地提
示用户输入这些值。但是,这种方法经常会导致安全性方面的问题,因为用户可以通过
无数次的尝试猜出正确的用户 ID 及密码。
另一种方法是使用应用程序的特殊用户 ID 和密码。这种方法隐含了这样的假定:只有
能够访问数据源或工作站的用户才能够访问应用程序。
如果选用域管理安全机制,那么必须将 OpenConnection 或EstablishConnection 方法
的 connect 参数中的 UID 和 PWD 保留为空的。这种类型的安全机制将 Windows NT
登录 ID 及密码传递到数据源。这些值是在用户登录到 Windows 95 或 Windows NT 时
获取的。如果系统管理员实现了集成的或混合的安全机制,那么,如果用户被授予访问
数据源的权限,那么这种方法将允许她登录到数据源。例如,在前面的例子中,域管理
安全机制的 connect 参数设置如下:
Conn$ = "DSN=MyRemote;UID=;PWD=;"

指定缺省数据库
与 rdoConnection 对象关联的数据库最初由 DATABASE 连接字符串参数决定。如果没
有提供这个串,那么用户的缺省数据库将由服务器管理员分派,或者由 DSN 指定的。
在连接完成之后,也可以通过执行 Transact SQL (TSQL) USE database 查询来改变缺
省数据库。无论在什么情况下,都必须具有访问被选定数据库的权限,否则将会引发可
以捕获的错误。
例如,以下代码将缺省数据库改为 cn 连接上的 "Pubs":
cn.Execute "Use Pubs"
将连接字符串传递到 RDO 或 RemoteData 控件
一旦完成,连接字符串作为以下各项之一传递:
■ OpenDatabase 方法的 Connect 参数。
■ RemoteData 控件的 Connect 属性。
■ rdoConnection 对象的 Connect 属性,如果使用 EstablishConnection 方法。
 为了将这些信息传递到 RDO,RemoteData 控件提供若干专门属性,但是,如果连接字
符串作为 RemoteData 控件的 Connect 属性传递,那么就不需要额外的属性。连接字
符串提供的值的优先级高于 RemoteData 控件属性中提供的值。

(二) 使用已注册的数据源名称(DSN)
建立连接的最简单的方法是使用已注册的数据源名称 (DSN) 提供关于服务器的信息。
由于 DSN 不包含用户及应用程序的信息,所以必须在连接字符串中提供它们,或者用
提示选项从用户那里取得这些信息。
DSN 项通过 Windows 控制面板或 rdoRegisterDataSource 方法创建。但使用
“Windows 数据源”对话框是创建、修改或删除数据源名称的首选方法。要打开该对话
框,请双击 Windows 控制面板中的 32 位 ODBC。
在以下各项中,可以通过名称引用已注册的 DSN:
■ OpenConnection 方法的 dsName 参数。
■ RemoteData 控件的 Database 属性。
■ 作为 connect 参数传递到 OpenConnection 方法的连接字符串。
■ 使用 EstablishConnection 方法的 rdoConnection 对象的 Connect 属性。
■ RemoteData 控件的 Connect 属性。
 连接字符串总是处于优先级高的位置。
注意,DSN 包括一些参数,这些参数只能通过使用 ODBC API 函数以编程方式进行设
置,或者使用 rdoRegisterDataSource 函数或 Windows 控制面板设置。它们不能通过
连接字符串参数来设置。例如,OEMTOANSI 和 NETWORK 就不能用连接字符串设置。
没有安装或注册的 ODBC 驱动程序是不能使用的。虽然安装和注册已经是 Visual
Basic version 5.0 安装程序的一部分,但仍必须包含在应用程序的设置过程中。
下述的例子在名为 SEQUEL 的服务器上注册了一个名为 Example 的 SQL Server 数据
源。然后在该服务器上打开 WorkDB 数据库。
Private Sub RegisterDataSource()
Dim en As rdoEnvironment
Dim cnTest As rdoConnection
Dim strAttribs As String
' 建立关键字字符串。
strAttribs = "Description=" _
  & "SQL Server on server SEQUEL" _
 & Chr$(13) & "OemToAnsi=No" _
 & Chr$(13) & "SERVER=SEQUEL" _
 & Chr$(13) & "Network=DBNMPNTW" _
 & Chr$(13) & "Database=WorkDB" _
 & Chr$(13) & "Address=\\SEQUEL\PIPE\SQL\QUERY"

' 创建新的注册 DSN。
rdoEngine.rdoRegisterDataSource "Example", _
  "SQL Server", True, strAttribs
' 打开数据库。
Set en = rdoEngine.rdoEnvironments(0)
Set cnTest = en.OpenConnection( _
 dsname:="Example", _
 Prompt:=rdDriverNoPrompt, _
 Connect:="UID=;PWD=;")
End Sub

(三) 创建非DSN方式的RDO连接
建立 RDO 连接的另一个方法是:在 OpenConnection 方法的 Connect 参数中提供所有
必需的信息。该方法有许多优点:
■ 可以不创建 DSN,这样就能简化客户应用程序的设置和安装。
■ 不必在系统注册表中查找 DSN,加快连接速度。
■ 更有效到控制服务器及其它连接参数,从而提高应用程序和系统的安全性。
 如果选择使用非 DSN 方式的连接,则连接字符串中必须包含服务器和驱动程序的名
称,以及与用户有关的所有信息。但是,如果选择这个选项,只能使用缺省的
OEMTOANSI (Off) , NETWORK(命名管道)以及其它一些设置,它们只能在创建 DSN 时
设置。
如果选择创建一个非 DSN 方式的连接,则必须传递一个零长度字符串作为
OpenConnection 方法的 dsName 参数,或传递 RemoteData 控件的 Database 属性。
这样就通知 ODBC 驱动程序:希望创建一个非 DSN 方式的连接。另外,请注意连接字
符串的顺序要正确。DSN 参数必须在 SERVER 和 DRIVER 参数之后,如下所示。
下述示例说明如何创建一个非 DSN 方式的连接,以连接到名为 MyServer 的
Microsoft SQL Server 数据库。
Dim Cn As rdoConnection
Dim En as rdoEnvironment, Conn As String
Set En = rdoEnvironments(0)
Conn$ = "UID=Holly;PWD=Huskador;" _
 & "DATABASE=MyDb;" _
 & "SERVER=MyServer;" _
 & "DRIVER={SQL SERVER};DSN='';"
Set Cn = En.OpenConnection(dsName:="", _
 prompt:=rdDriverNoPrompt, _
 connect:=Conn$)

(四) 设置rdoConnection选项
在使用 OpenConnection 或 EstablishConnection 方法之前,可以设置一系列的属性
和选项,它们将影响到:怎样建立连接及如何处理使用这些连接的查询。通常这些选项
由 rdoEnvironment 属性决定。但是,如果要创建了一个独立的 rdoConnection,则必
须在建立连接前预先设置下述与缺省值不同的 rdoConnection 属性。
下表详细列出了在试图建立连接之前必须设置的 rdoConnection 选项,它们的缺省
值,以及它们的功能。
rdoConnection 选项
rdoConnection 属性  缺省值  作用

LoginTimeout    15    放弃该次连接企图之前等待的秒数。
CursorDriver   rdUseIfNeeded  与对象相联的所有查询使用哪一个游标库(如果有的
话)。
Connect    ""    用于创建连接的 ODBC 参数,即 连接字符串。
Name    ""    用于该连接的 DSN 名称。

(五) 使用rdoConnection对象事件
rdoConnection 对象会触发一系列的事件过程,有了这些事件过程,对连接以及与该连
接相联的查询的管理将更加简单。在以下情况会触发这些事件:
rdoConnection 事件 什么时候被触发

BeforeConnect   在调用 ODBC SQLDriverConnect 函数之前。
Connect    连接操作完成之后,无论操作的成败。
Disconnect   连接断开之后。
QueryComplete   异步查询完成之后。
QueryTimeout   在某个查询的超时时期已经超出了rdoConnection对象的QueryTimeout
属性之后。
WillExecute   RDO 试图执行这些查询之前。
所有执行于相联的 rdoConnection 上的查询都会触发 QueryComplete, QueryTimeout
以及 WillExecute 事件。这也包括通过 OpenResultset 或 Execute 方法执行的那些
查询,以及从相关联的 rdoQuery 对象执行的查询。Query 参数是一个对象引用,表明
哪次查询触发了该事件。使用这个参数,该连接上的所有查询之需要写一个事件处理程
序,但是,仍然要为特定查询定制处理程序。当执行对 rdoConnection 对象本身的查
询时,RDO 将在内部创建了一个 rdoQuery 对象,而且,这个内部 rdoQuery 的引用将
作为 Query 参数传递。
使用 BeforeConnect 事件处理程序
BeforeConnect 事件提供了改变正被传递到 ODBC SQLDriverConnect 函数的连接字符
串的机会。在代码中可以添加原先没有提供的工作站 ID 或其它参数。通过筛选这些连
接参数,可以防止用户浏览未授权的数据源或用户名。
使用 Connect 事件处理程序
Connect 事件的作用是:让代码知道什么时候完成对于特定连接对象的连接操作,就是
说,该事件无论连接操作成功与否都会触发。每一个 rdoConnection 对象都可以通过
宣称,显示这个事件以及与连接有关的其它所有事件。
在连接时间较长的情况下,Connect 事件尤其有用,例如在广域网上。在使用
OpenConnection 或者 EstablishConnection 方法的 rdAsyncEnable 选项时,推荐使
用该事件,而不要去轮询 rdoConnection 对象的 StillConnecting 属性。如果连接操
作失败,ErrorOccurred 参数将设为 True。这时,代码需要检查 rdoErrors 集合,以
确定失败的原因。
使用 QueryComplete 事件处理程序
在使用异步查询时,必须为 QueryComplete 事件设置一个事件处理程序,该事件在查
询完成时被触发。它是对整个连接建立的,因此该连接上的所有查询都会触发同样的
QueryComplete 事件。
尽管绝大多数的 ODBC 驱动程序不支持同时执行多个的操作,支持的也不是没有,因
此,QueryComplete 事件处理程序必须能够知道已经完成的查询是哪一个。
QueryComplete 事件把一个对象引用传递到 rdoQuery,从而可以知道该查询的名称和
其它属性。如果查询未能建立连接,ErrorOccurred 参数将设为 True。这时,代码必
须检查 rdoErrors 集合,以确定失败的原因。
使用 QueryTimeout 事件处理程序
并非所有的查询都会立即完成,有的甚至几分钟内都不能完成。但是,除非设置
QueryTimeout 属性表明该查询所期望的执行秒数,否则 QueryTimeout 事件会在设定
的时间后被触发,缺省的时间为 30 秒。如果代码将 QueryTimeout 事件的 Cancel 参
数设置为 False,RDO 将使用 QueryTimeout 属性中的秒数重新启动查询超时时钟。这
个特性在广域网上尤其有用,在广域网上,不能保证总用相同的时间执行查询。
注意   在一个查询开始时,QueryTimeout 属性被传递到 ODBC 驱动程序,所以对它的
修改只会对下一次查询产生影响。
使用 WillExecute 事件处理程序
该事件触发于执行一个查询之前,无论是动作查询还是返回行的查询。可以捕获这个事
件,以禁止执行某些特殊查询,或者对 SQL 字符串作最后的调整。
Cancel 参数用来禁止查询。例如,可以预视该查询,以确认 WHERE 子句是足够的,从
而避免权全表扫描 (table scan)。另外,可能希望禁止用户既不提供姓也没有街道地
址,只用 "Smith" 对客户进行查询。Cancel 参数的缺省值为 False,但如果将其设为
True 的话,该查询将不会执行,RDO 将产生一个可以捕获的错误,表明该查询被取
消。
WillExecute 事件的一个很重要的功能是:可以用自己的代码对操作进行更改。就是
说,可以执行一系列自己的操作,包括存储过程在内,来完成需要的功能。例如,如果
远程数据库只允许通过存储过程执行更新,那么,可以捕获该事件,执行特定的参数化
存储过程来执行更新,然后用 Cancel 参数取消原来需要执行的自动操作。

(六) 异步打开连接
在某些情况下,特别是在使用 Internet、广域网 (WAN),或远程访问服务 (RAS) 连接
时,建立一个连接所需的时间可能会急剧增加。对于一个典型的 LAN 连接来说,几秒
钟已经足够了,WAN 可能要花几分钟甚至更多时间来进行连接。在试图进行这些连接
时,可以采取若干方法预先解决这个问题:
■ 将 LoginTimeout 属性设置为一个足够大的值,与延迟相匹配。
■ 在连接建立之前,使用 rdAsyncEnable 选项将控制返回到应用程序,因而用户能够
知道正在试图建立连接,而不会阻塞应用程序。
■ 建立一个事件过程以捕获 Connect 事件,而不是使用 StillConnecting 属性进行
轮询。
■ 使用 BeforeConnect 事件,警告用户连接可能要进行相当长的时间。
 这些方法最重要的特点是:让用户了解情况,并防止应用程序的阻塞。有的时候,应
用程序本身并没有错误,但是在试图建立连接时,看上去似乎不运行了,那么用户可能
会采取办法来取消这次操作,包括重新启动计算机在内。

(七) 使用独立的rdoConnection对象
使用 rdoEnvironment 对象并非创建连接的唯一途径,也可以用 Dim As New 语句声明
一个独立的 rdoConnection 对象。例如,下述代码创建了一个名为 MyCn 的
rdoConnection 对象。
Dim MyCn As New rdoConnection
 使用 User Connection 设计器,也可以创建自己的 Visual Basic 类,它继承了
rdoConnection 对象,并且用新设计的功能对该对象进行扩展。
在创建了一个独立的连接之后,可以为新的 rdoConnection 对象设置 Connect 属性,
以指定特定的服务器、游标驱动程序以及下述的其它属性。一旦这些属性被初始化之
后,就可以使用 EstablishConnection 方法与指定的服务器连接了。
例如,要创建一个独立的 rdoConnection 对象,并建立与 CustomerDB DSN 所指定的
服务器的连接,可以使用以下代码:
Dim MyCn As New rdoConnection
With MyCn
 .Connect = "DSN=CustomerDB;DATABASE=MAILING;" _
    & "UID=;PWD=;"
 .CursorDriver = rdUseNone
 .LoginTimeout = 5
 .EstablishConnection (rdDriverNoPrompt)
End With
 如果希望将新的独立的 rdoConnection 与特定 rdoEnvironment 相联,必须使用 Add
方法将其添加到与选定的 rdoEnvironment 对象相联的 rdoConnections 集合。例如,
以下代码将 MyCn 连接添加到缺省的 rdoEnvironment 对象中:
rdoEnvironments(0).rdoConnections.Add MyCn

使用 Romove 方法,可以从 rdoEnvironments 集合中删除成员。
详细信息   请参阅《联机手册》的《语言参考》中的“EstablishConnection 方法”
部分。

(八) 使用rdoConnection对象
在创建了 rdoConnection 对象之后,可以用它:
■ 对选定的 rdoConnections 集合使用 Add 方法,将独立的 rdoConnection 对象添
加到指定的 rdoEnvironment 事务作用域中。
■ 使用 OpenResultset 或 CreateQuery 方法创建 rdoResultset 或 rdoQuery 对
象。
■ 执行动作查询,以更新数据库数据,修改数据库模式,或执行管理功能。
■ 执行存储过程,管理其结果集、输出参数以及返回值。
■ 对 rdoConnection 对象使用 Close 方法,将连接从数据源断开并释放其资源。
 请注意,rdoConnection 对象没有设置事务的空间,这由 rdoEnvironment 进行管
理。不能对 rdoConnection 对象使用 BeginTrans , CommitTrans 或 RollbackTrans
等事务方法,不能象使用 rdoEnvironment 对象那样用 rdoConnection 对象。

(九) 与RDO数据源连接失败
以下种种原因可能导致连接创建失败:
■ 不具备访问数据源或网络的权限。
■ 不合适的网络连接或权限。
■ 数据源丢失或无效。
■ 没有驱动程序,或其安装或注册是不正确的。必须安装并注册包含在内的 ODBC 和
网络协议驱动程序。
■ 当前的应用程序,或者其它的应用程序,建立了太多的连接。
■ 网络或远程服务器停机或者过于繁忙,以至于不能在 LoginTimeout 属性所期待的
指定时间内作出响应。
 例如,在与 Microsoft SQL Server, Oracle 或其它数据源进行连接时,允许的同步
连接数量可能会受到授权协议、资源限制或数据库设置等因素的约束。如果怀疑是否所
有可用的连接都被分配,请咨询服务器管理员。
无论在什么情况下,代码中都必须包含足够的错误处理程序,以处理各种可能发生的意
外情况。有一些问题是由于设置产生的,有些是暂时的,就是说,一旦远程系统的资源
空闲或 LAN, WAN 的通信量减少时,它们会自动更正。
由 RDO 捕获的错误通常是一般的 ODBC 错误,几乎不能告诉任何有关错误的根本原
因。但是,错误的详情会保存在 rdoErrors 集合中。rdoErrors 集合中的每一个
rdoError 错误都包括了导致错误原因的详细信息。检查 rdoError 对象属性可以获得
关于错误的重要信息,包括描述字符串、本地错误号(由 SQL Server 返回),以及与
ODBC API 错误管理机制有关的其它值。
详细信息  请参阅《联机手册》,以获取关于 rdoError 对象的结构和属性的详细信
息。

(十) 使用rdoTable对象
使用 rdoTable 对象映射表或数据源中的列,或者为表中的所有行创建 rdoResultset
对象,但 RDO 建议不要这样做。在绝大多数的情况下,不需要从一个数据库表中检索
所有的行到工作站的内存中。许多远程数据库表是极为庞大的,以至于不能整个地卸载
到工作站。
如果需要检查数据源的表结构或列的细节,可以使用 rdoTables 集合。然而,为了提
高性能,在 rdoTables 集合被引用之前不请求来自数据源的表元数据 (table meta
data)。在打开了 rdoConnection 之后,可以列出这些表,并将它们的名称放到一个
ListBox 控件中,如下例所示(假设 cn 是一个打开的 rdoConnection):
Dim tb As rdoTable
For Each tb in cn.rdoTables
 List1.AddItem tb.Name
Next
 每一个 rdoTable 对象都包含一个 rdoColumns 集合,这个集合中包括了关于每一列
的数据类型、大小的详细信息。虽然可以创建 rdoTable 对象的 rdoResultset,但
是,该查询却只能返回所有的数据行,最好不要这样用。

(十一) 关闭不需要的RDO连接
当不再需要访问远程服务器时,应该考虑使用 Close 方法关闭该连接。在应用程序中
止之前,有时关闭连接是没有意义的,但是,在很多的方案中,为了让尽可能多的用户
能够同时连接到系统,通常在空闲或不再需要时将连接关闭。如果应用程序以后需要的
话,则可以重新打开连接。
连接断开之后,服务器上所有的实例所有的对象都被释放。例如,服务器端的游标以及
创建于 TempDB 的所有对象都将被服务器放弃。如果需要保持对这些对象的访问,就不
能关闭相应的连接。
RDO 不能缓存刚刚关闭的连接,以备以后使用,从这个意义上来说,它不能对连接进行
管理。在使用 Close 方法关闭一个连接之后,或对 rdoConnection 对象的最后一次引
用超出了作用域之后,该连接会被立即永久性地关闭。

七. 使用 RDO 提交查询如何建立并提交查询(包括参数查询)。
在多数情况下,创建数据库前端的主要原因是用于检索、操作和显示数据,并提交所作
的修改。程序中的大多数代码都涉及到建立查询、处理结果集,以执行这些基本操作。
在需要远程数据库的数据时,需要提交一个查询,指定所需的数据行。在更新数据时,
则要提交更多的查询,将新的数据或所作的修改传递到远程数据库。如果应用程序需要
执行管理任务,则必须提交包含高级指令的查询来运行服务器端的实用程序,以建立新
的用户,或者运行数据库的修复或配置过程。实质上,RDO 所有与交互有关的方面都涉
及到提交查询,基本的工作模式是:先提出问题,然后处理以结果集的形式返回的答
案。
通常,RDO 查询处理可以分为两个方面:
■ 提交由 SQL 语句构成的查询,它描述了结果集,或引用了一个存储过程。
■ 处理结果行、参数以及返回值,也就是各种各样的结果集。
提交查询的过程包括:建立一个 SQL 语句,其中包含所有必需的参数,并将该查询送
到远程服务器上处理。在决定如何提交查询时,必须先决定它返回时采用的形式。根据
应用程序的需要,需要考虑以下的问题:
■ 应用程序需要滚动或浏览数据吗?如果需要滚动,需要从两个方向滚动吗?
■ 是否需要更新数据?如果是,能够使用动作查询来改动它吗?用户是否具有修改数
据的权限?
■ 有多少用户可能同时试图对相同的数据进行操作?能够控制那些应用程序吗?他们
是否会与数据共享机制合作?
■ 该查询将返回多少行数据?系统有能力保存这些行吗?网络是否具有传送这些行的
带宽?
■ 这些行如何返回?作为书签还是静态数据?
■ 必须传递参数到该查询吗?
■ 查询是返回行、输出参数还是返回值,还是仅仅执行一次操作?
■ 在应用程序执行的过程中,该查询可能要运行很多次还是仅仅执行几次?
 下列主题将帮助解决这些问题,解释各种情况下取得数据的正确策略。
管理 RDO 查询
在打开连接之后,就可以提交查询到远程服务器上执行。无论使用什么方法,RDO 都将
使用下面两个 ODBC API 函数中的一个来执行查询并检索结果集。
■ 如果该查询只需运行一次,那么对 rdoConnection 对象使用 OpenResultset 或
Execute 方法执行固定的 SQL 查询,从而创建 rdoResultset 对象或者执行动作查
询。这种选择将导致查询使用 ODBC API SQLExecDirect 函数。也可以采用
rdExecDirect 选项和 Execute 或 OpenResultset 方法,强制使用 SQLExecDirect 函
数。
■ 如果使用的查询需要多次运行,并且可能需要参数,那么用 CreateQuery 方法实例
化一个 rdoQuery 对象,该对象可以根据需要重新使用,并且允许在每次执行查询时修
改一个或多个参数。创建了 rdoQuery 之后,对它使用 OpenResultset 或 Execute 方
法,创建一个 rdoResultset 或执行动作查询。为了修改查询参数,可以改变
rdoParameter 对象的设置。这种选择将导致查询使用 ODBC API SQLPrepare 和
SQLExecute 函数。
■ 如果使用存储过程来访问数据或执行动作查询,那么可以使用 User Connection 设
计器来连接特定的存储过程,并创建自定义的 rdoQuery 对象来访问它们。这也可以用
代码实现。这种选择将导致查询使用 ODBC API SQLPrepare 和 SQLExecute 函数。与
数据库中存储的 DAO 查询不同,每当应用程序执行时,这些 rdoQuery 对象被重新创
建。
 注意   为了向后兼容,Visual Basic version 5.0 仍然支持已过时的 Visual Basic
version 4.0 的 rdoPreparedStatement 对象,但在将来的开发中建议不要使用。最好
用 rdoQuery 对象代替所有的 rdoPreparedStatement 对象的引用。
(一) 创建rdoResultset对象
尽管 RDO 有若干个选项使检索结果集和返回参数的操作更简单,但它们最终都会导致
创建一个 rdoResultset 对象。例如,以下所有的方法都可用于创建和管理
rdoResultset 对象:
■ 对 rdoConnection 使用 OpenResultset 方法。这用于提交在应用程序中只需执行
一次的查询。返回的数据行放在 rdoResultset 对象中。
■ 对 rdoQuery 对象使用 OpenResultset 方法。这种方法用于提交在应用程序中希望
多次执行或需要参数的查询。返回的数据行放在 rdoResultset 对象中,所有查询返回
的参数通过 rdoQuery 对象的 rdoParameters 集合被引用。
■ 创建一个独立的 rdoQuery 对象。设置其属性并将它与特定的具有
ActiveConnection 属性的连接相联。然后使用 OpenResultset 方法创建 rdoQuery 的
rdoResultset。可以通过修改 ActiveConnection 属性引用其它连接,不需要另外创建
rdoResultset 对象就可以重新执行查询。
 详细信息   请参阅本章后面的“创建 RDO 参数查询”部分。

(二) 使用OpenResultset方法
无论要执行的查询属于什么类型,无论创建什么类型的结果集,都必须使用
OpenResultset 方法建立一个 rdoResultset,用来获取并查看数据行。用于创建
rdoResultset 的特定的游标类型和连接都取决于被 OpenResultset 方法引用的对象的
属性。例如,如果要对 rdoQuery 执行 OpenResultset 方法,那么 RDO 将使用
ActiveConnection 属性指定的连接以及 CursorDriver 属性指定的游标类型。
OpenResultset 方法接受以下参数:
■ 在对 rdoConnection 对象使用 OpenResultset 方法时,必须指定 name 参数,通
常名称参数字符串指定了 SQL 查询。对 rdoTable 对象使用 OpenResultset 时,SQL
语句是从 rdoQuery 对象的 SQL 属性得到的,因此不再需要 name 参数。在对
rdoTable 对象使用 OpenResultset 时,假定该查询包含了指定表的所有行。如果 SQL
查询需要包含参数,请参阅本章后面的“创建 RDO 参数查询”部分,以获取如何为参
数查询选择正确的 SQL 语句的详细信息。
■ type 参数指定了游标的类型,除非将 CursorDriver 的属性指定为 rdUseNone 使
得该连接上的游标创建无效。如果没有指明游标类型,假定为 rdOpenForwardOnly。
type 参数也可以指定游标类型为键集、动态、静态或只能向前的类型。
 注意   并非所有的游标库都能实现全部游标类型。例如 ODBC 客户端的游标库
(rdUseODBC) 就不能创建键集或动态游标。当驱动程序不能创建某种游标时,通常会采
用退却策略:选择一种可以创建的游标类型。

■ The locktype 参数指定锁定类型以支持并发。通常,该选项决定了行和页被远程服
务器锁定的时机和方式。例如,使用 locktype 参数,可以选择几种开放锁定,一种保
守式锁定,或根本不锁定,正如选择 rdConcurReadOnly(缺省)那样。关于详细信
息,请参阅《联机手册》的《语言参考》中的 “OpenResultset 方法”。
■ options 参数在该查询是否将异步执行,以及用来执行查询的 ODBC 函数。在缺省
时,查询将同步执行,并使用 SQLPrepare 和 SQLExecute API 函数。
选择一种游标类型
使用 OpenResultset 方法的参数或 rdoQuery 对象的参数时,可以指定游标的类型
(如果有)以及 rdoResultset 对象的其它属性。然而,很多情况下游标并非检索数据
的最佳方法,因为它们会对性能和资源利用产生负面影响。
详细信息   请参阅《联机手册》的《语言参考》的 “OpenResultset 方法”、“Type
属性”和“SQL 属性”。

(三) 编写SQL语句
SQL 属性包含了结构化的查询语言语句,它们决定了在执行查询时如何对行进行选择、
分组和排序。使用查询可以选择数据行。也可以定义动作查询修改数据而不返回数据
行。
在设计时,不能为 RemoteData 控件的 SQL 属性提供一个表的名称,象使用 DAO 那
样。但是,可以使用一个形如 SELECT * FROM <表> 的简单查询,或者在运行时置入
rdoTables 集合,并使用该集合中的一个表名。一旦 rdoTables 集合与一个活动连接
相联并被引用,置入也就完成了。
查询的 SQL 语法必须遵循数据源查询处理程序定义的 SQL 方言。ODBC 接口支持的
SQL 方言由 X/Open 标准定义。通常,驱动程序扫描一个 SQL 语句,查寻标识非标准
运算符的转义序列,时间标记、文字和函数等都属于非标准的运算符。
当需要从一个查询返回数据行的时候,通常要在 SQL 属性中提供一个 SELECT 语句。
SELECT 语句中指定:
■ 要返回的每一列的名称,或者用 "*" 表示返回的表的所有列。可能引起混淆的列名
必须包括对应表的名称。也可以指定对选定列执行执行算术运算或其它函数的统计表达
式。统计列或计算列也必须有别名,从而能够在 rdoColumns 集合中提供它们的名称引
用。
■ 查询需要用到的表的名称。如果要指定多个表,则必须提供一个 WHERE 子句,表明
哪些列用于交叉引用表中的信息。通常这些数据列具有相同的名称和意义。例如表
Customers 和表 Orders 中的 CustomerID 列就可以将两个表通过公共的数据列连接起
来。
■ 任选地,WHERE 子句说明如何连接两个指定的表,如何限制和筛选返回列的数目和
类型。可以在 WHERE 子句中使用用户提供的参数,从而在各次查询中检索不同的信
息。如果需要在运行时提供 WHERE 子句的条件,那么必须创建一个参数查询。
■ 其它的任选子句,例如 ORDER BY 子句用于将数据列按照指定方式排序,GROUP BY
子句用于将数据列分组到相关的集合中。
 每种 SQL 方言支持不同的语法和辅助子句。请参阅远程服务器提供的文档以获取详细
信息。
详细信息   请参阅“创建 RDO 参数查询”。

(四) 创建RDO参数查询
如果需要执行的 SQL 查询的 WHERE 子句中包含一个或一个以上的参数,那么可以使用
rdoQuery 对象来运行该查询,并且在每一次执行时管理这些参数。对于需要重复运
行,或对于几个连接运行的查询,这种方法非常有用,尤其是执行参数化的存储过程的
时候。
提示   可以创建自己的查询,与参数一起构成一个完整的 SQL 语句。有时这种方法可
能是创建参数查询的唯一方法,特别是查询很复杂,或以特殊方式使用远程数据库语法
的时候。
 任何情况下,提交的 SQL 语句都必须在语法上正确。与参数查询相关的许多问题都源
于远程服务器所要求的原始 SQL 编码或者下述的 ODBC SQL 语法不正确。提交语法不
正确的查询会导致各种各样的问题,包括远程引擎返回的语法错,以及 RDO 无法创建
rdoParameters 集合等。
用 rdoParameters 集合管理参数
如果希望 RDO 使用 rdoParameters 集合来管理参数,可以在 SQL 语句的每一个参数
的位置包含一个问号。"?" 用作输入、输出以及输入/输出查询参数的占位符。代码通
过设置 Direction 属性表明了占位符与参数之间的对应关系。RDO 和 ODBC 接口自动
管理这些参数,并按照预先定义的数据类型将每个参数绑定到一个 rdoParameter 对
象。有时候代码可能要强制指定某些参数的数据类型。尤其是在查询中包含表达式,表
达式的自变量作为参数传递的时候。
所有参数都被标记和标识之后,RDO 和 ODBC 接口将自动创建与 驱动器有关的 SQL 语
句,以及一个 rdoParameters 集合以管理参数值和数据类型。在大多数情况下,不必
为用作参数的字符串加引号,也不必关心其它的格式问题。
注意   在创建 rdoQuery 对象时,并没有进行语法检查。只有执行该查询或者访问
rdoParameters 集合时,才会编译它并为其参数赋值。如果语句中有语法错误,可能会
触发可以捕获的 40054 “非法参数”错误,或其它一些 ODBC 错误。有些情况下,
rdoParameters 集合没有成功地创建,因此在被引用时可能会产生可以捕获的错误,表
明该对象不存在。
 提示  虽然可能在有效 ODBC 驱动程序列表中发现 DAO (Access) 数据库的 ODBC 驱
动程序,但它们不符合 ODBC Level II。可以使用 RDO 向该驱动器提交查询,并返回
结果集,但它不能象遵循 Level II 的驱动程序那样创建 rdoParameter 对象或者管理
查询参数。
为参数查询选择正确的 SQL 语法
在编写 rdoQuery 对象的 SQL 属性或 OpenResultset 方法的 name 参数时,可以选择
以下三种风格之一来编写参数查询:
■ 连接串。使用 Visual Basic 连接运算符 (&) 来建立 SQL 语句及其参数。该语句
可以传递到 OpenResultset 方法的 name 参数或 rdoQuery 对象的 SQL 属性。连接串
查询看起来类似于:
 sSQL = "Select Name, Age From Animals " _
  & " Where Weight > " & WeightWanted.Text _
  & " and Type = '" & TypeWanted.Text & "'"

■ 原始 SQL 语法(Native SQL)。远程服务器使用的 SQL 语法。可以执行自己的查
询或存储过程,并  且通过连接符、占位符或二者结合来传递参数。占位符所标记的参
数由 RDO 作为 rdoParameter 对象进行管理。SQL 参数查询看起来类似于:
 sSQL = "Select AU_LName from Authors" _
  & " Where AU_Fname = ?"
 - 或者 -
 sSQL = "Execute MyStoredProc 'Arg1', 450, '" _
  & Text1 & "'"
 - 或者 -
 sSQL = "Execute MyStoredProc ?, ?, ?"

■ ODBC CALL 语法。用来调用存储过程,该存储过程返回一个返回状态或输出参数。
可以为每一个输入、输出或输入输出参数定义一个占位符,这些占位符自动被映射到
rdoParameter 对象。必要时也可以在其中混入一些连接运算符。ODBC CALL 参数查询
看起来类似于:
 sSQL = "{call ParameterTest (?,?,?) }"
 - 或者 -
 sSQL = "{? = call ParameterTest (?,?,?) }"
 - 或者 -
 sSQL = "{? = call CountAnimals (?, ?, 14, 'Pig')}

注意   SQL Server ODBC 驱动程序要求所有非捆绑的参数(在代码中被连接到查询的
参数)都显示在占位符参数(那些以“?”标记的参数)的右边。如果不这样做,将产
生一个可以捕获的错误:"Wrong number of parameters"。
ODBC CALL 语法的优点及局限
使用 ODBC CALL 语法有许多的优点。例如,ODBC 使用开放数据系统远程过程来执行查
询。参数以它们原始的形式传递,不需要进行分析或类型转换。这也意味着 ODBC 不需
要为处理查询而“预先准备”,因为它已经以存储过程的形式存在于远程服务器上了。
这样调用的效率更高,并且数据库之间的可移植性更强。
使用 rdExecDirect 执行参数查询
在执行过程时,rdExecDirect 选项强制 RDO 使用 ODBC API SQLExecDirect 函数。这
样就省略了 ODBC API SQLPrepare 这一步骤,后者用于创建一个执行查询的临时过
程。当需要的 SQL 语句可以被远程服务器接受,但不能被 ODBC 接口接受时,可以使
用该选项。但是,在执行存储过程参数查询时不能使用它,因为这会妨碍参数进行正确
的类型绑定。
注意   在有些情况下,在关闭连接之前不能删除 ODBC 接口创建的临时存储过程。使
用 rdExecDirect 选项可以避免这个问题。
语法选项总结
下表总结了使用三种语法风格时可用的选项:
语法选项

功能       Native SQL语法 ODBC Call语法  Concatenated strings

能否传递未引用存储过程的原始SQL?   是   否    是
可以执行存储过程?       是   是    是
参数能否使用 ? 占位符?      是   是    否
管理返回值        否   是    否
管理输出参数          否     是    否
SQL语句是否可以包括多个Select 语句? 是   否    是
注意   RDO 管理 RDO 参数集合中的查询参数的能力是有条件的,需要 ODBC 接口能够
正确地分析查询,并且能够确定参数的正确类型。在有些情况下,ODBC 驱动程序管理
器无法正确标识一个 SQL 语句中的所有参数。这时,可以将该语句转换成一个存储过
程,即便是临时的,可能就可以使查询成为可用的。
提示   虽然 ODBC Call 语法可以用于不需传递参数或没有返回值的情况下,通常,只
有在需要获得存储过程的返回状态和输出参数时才使用它。
编写一个典型的参数查询
参数查询仅仅是将用户或应用程序提供的参数代入普通的查询中。该查询通常是
SELECT 语句,但也可以是 INSERT, UPDATE 或 DELETE 查询。下面的例子说明如何编
写带有一个参数的简单 SELECT 查询。该查询从 Pubs 范例数据库中查询特定姓名的作
者。
首先,设置一个 SQL 查询,并用 ? 标记其中每一个参数。
QSQL$ = "SELECT * FROM Authors WHERE Au_Lname = ?"

然后,创建一个 rdoQuery 对象以管理该查询及其参数。
Set PSAuthors = cn.CreateQuery("",QSQL$)

下一步,使用以下代码将用户输入的值 (Text1.Text) 插入到该查询。
PSAuthors.rdoParameters(0) = Text1.Text
 注意,这里的 rdoParameters 对象可以是隐含的,因为它是 rdoQuery 对象的缺省集
合。与其等价的代码是:
PSAuthors(0) = Text1.Text
 然后,创建一个 rdoResultset 对象,取得满足条件的数据列(名字与参数值一致的
数据列)。
Set MyRs = CpwPSAuthors.OpenResultset()
 如果用户改变了参数值 Text1.Text,可以对 rdoResultset(MyRs) 使用 Requery 方
法,重新应用新的参数并重新执行查询,并不需要重建 rdoQuery 对象。
PSAuthors(0) = Text1.Text
MyRs.Requery
 当 RDO 执行 Requery 方法时,它会更新与查询参数相联的 rdoParameters 集合中的
参数值,清除当前的结果集,将查询送到数据源执行,并创建一个新的
 rdoResultset。
请记住该查询是在什么时候首次创建的,RDO 和 ODBC 层在远程服务器上创建了一个临
时的存储过程,用来接收参数。每次执行该查询时,只需将新的参数传递到这个临时的
查询并执行它。
提示   如果使用了带 rdAsyncEnable 选项的 OpenResultset 方法,在使用 Requery
方法时也要设置该选项。
注意   在执行不带参数的存储过程时,不要在 SQL 语句中包含括号。例如,为了执行
不带参数的 "MySP" 过程,可以使用如下语句:
{Call MySP }
如果用户更改了参数值,那么可以使用 rdoResultset (MyRs) 的 Requery 方法,用新
的参数值重新执行该查询。
Cpw(0) = Text1.Text
MyRs.Requery
参数的连接
将参数连接到 SQL 语句字符串,也可以为 SQL 查询指定参数。例如,要提交采用这种
方法的一个查询,可以使用以下代码:
QSQL = "SELECT * FROM Authors WHERE Au_Lname = '" _
 & Text.Text & "'"
Set MyRs = Cn.OpenResultSet(QSQL)
 这种情况下,不会创建 rdoParameters 集合,也不能引用它。要修改查询参数,那么
在查询执行的时候或使用 Requery 方法之前,必须用新的参数值重建该 SQL 语句。另
外,除非使用了 rdExecDirect 选项,否则,每次使用 OpenResultset 方法时,RDO
都将创建一个新的临时存储过程,并执行该查询。
详细信息   请参阅《联机手册》的《语言参考》中的 “rdoQuery 对象”、
“rdoParameter 对象”、“Requery 方法”和“OpenResultset 方法”部分。关于存
储过程的其它信息,请参阅“使用 RDO 执行存储过程”。

八. 使用 RDO 执行存储过程如何满足存储过程的特殊要求。
存储过程是管理和保护复杂的关系数据库最有效的方法之一。在支持大型数据库、大量
的用户或同时支持二者的系统中,它的地位非常重要。使用存储过程,可以防止重要的
数据被随意访问,拦截那些图谋不轨的家伙,以及动机清白但却具有破坏性倾向的“好
人”。在很多情况下,存储过程是访问或更新数据的唯一途径,对基本表的访问是严格
禁止的。
RDO 最强大而且最重要的特性之一是:它可以执行和管理由存储过程返回的结果,无论
它们有多么复杂。以下主题讨论了如何最充分地利用这种特性。
存储过程通常要求一个或一个以上的参数,有的存储过程返回数据行,但并非全都如
此。RDO 提供了很多方法处理这些情况;但是,如果需要获取存储过程的状态或输出参
数的话,则必须使用 ODBC CALL 语法,“创建 RDO 参数查询”一节对此进行了描述。
一个存储过程可以返回任意个数的结果集,而且一个存储过程还可以调用其它的存储过
程。代码必须准备管理所有的结果集,有的结果集包含数据行,有的只返回参数,有的
只返回受到影响的数据行的数目,还有一些包括以上三者。
(一) 使用RDO编写一个典型的存储过程
这个例子将展示一个存储过程必需的步骤,该过程带有两个参数。存储过程的参数可以
是输入,输出或输入输出参数。在绝大多数的情况下,ODBC 驱动程序可以自动确定参
数的类型并正确指定其 Direction 属性。本例执行的是 sp_password 过程,该过程可
以在 SQL Server 系统中改变用户的密码。假定所要改变的密码属于当前用户,因为只
有系统管理员才能修改用户的密码。
这个过程接受两个输入参数并返回一个返回值。可以使用 Execute 方法运行该查询,
但返回值将会丢失。这样就没有简单的方法可以得知密码的改变是否成功。为了获取返
回值并创建一个可以重复执行修改密码的 rdoQuery,可以用以下代码:
Dim CPw As rdoQuery, QSQL As String
QSQL$ = "{ ? = call sp_password (?, ?) }"
 接下来的一行代码创建了 rdoQuery,并将其命名为 SetPassword。SQL 属性设置为上
面所定义的 QSQL 查询。这行代码只需执行一次。新的 rdoQuery 对象会自动追加到
rdoQueries 集合,可以重复地调用它。
Set CPw = cn.CreateQuery("SetPassword",QSQL$)
 下面一步设置了 Direction 属性,表明参数是输入、输出还是输入输出参数。缺省的
Direction 是 rdParamInput,但在绝大多数情况下根本不需要设置 Direction 属性,
因为 ODBC 驱动程序可以通过存储过程确定这个值。
参数的序数取决于它们在 SQL 语句中出现的次序。在本例中,“0”号参数是返回值
(? = ),第一号是第一个输入参数,第二号是第二个输入参数。可以看出来,
rdoParameters 集合是从零开始计数的。 以下的代码引用了 rdoParameters 集合,该
集合是 rdoQuery 对象的缺省集合。
Cpw.rdoParameters(0).Direction = rdParamReturnValue
 接下来的这一步设置了二个输入参数,RDO 在查询执行时将它们插入到该查询中。请
注意,rdoParameters 是缺省的集合,因此在以下代码中被略去了。
Cpw(1) = "clyde"  '设置第一个输入参数。
Cpw(2) = "framis"  '设置第二个输入参数。
 在参数的传递方向和值都设置好之后,如果没有返回值,可以使用 Execute 方法运行
该查询,如果这个过程包括一个或多个 SELECT 语句,使用 OpenResultset 方法:
Cpw.Execute()
 过程执行完之后,立即检查 rdoParameters 集合以获取返回值:
If Cpw.rdoParameters(0) <> 0 Then
 Msgbox "Could not change password"
End If

(二) 获取存储过程的输出参数
在 SELECT 语句中使用与参数查询相同的方法,也可以从一个过程中获取输出参数。
 获取输出参数
1. 使用 ODBC 转义语法为输出和输出参数以及返回值建立占位符。
2. 对于不能自动确定参数方向的 ODBC 数据源,代码必须设置每一个 rdoParameter
对象的 Direction 属性,表明该参数如何使用。
3. 因为 ODBC 数据源不能自动(或者说是正确地)确定单独的参数的数据类型,所以
在代码中必须设置每一个 rdoParameter 对象的 Type 属性,表明其数据类型。
4. 执行该查询。
 查询完全执行完毕后,从 rdoParameters 集合检索输出参数值。
编写带有输出参数的存储过程
本例执行了一个存储过程,该过程带有二个输入参数,返回二个输出参数以及一个返回
值参数。注意,SQL 查询在 rdoQuery 中使用了 ODBC 语法,如果要求存储过程返回输
出参数,那么这是必要的。
Dim SQL As String, MyOutputVal1 As Variant
Dim MyOutputVal2 As Variant, MyRetValue As Variant _
rs As rdoResultset
Dim Ps As rdoQuery

'使用命名自变量打开连接。
Dim cn as New rdoConnection
With cn
 .Connect = "dsn=Sequel;uid=;pwd=;database=workdb"
 .EstablishConnection Prompt:=rdDriverNoPrompt
End With

'使用 ODBC 参数自变量语法。
'注意每一个输入参数都用“?”标识,
'其中一个对应于返回值,
'两个分别对应于每一个输入参数,
'另外两个对应于每一个输出参数。

SQL = "{? = call TestOutputRS (?, ?, ?, ?) }"

'创建可以重复使用的 rdoQuery。
Set Ps = cn.CreateQuery("PsTest", SQL)

'为每一个输出参数和返回值参数
'设置参数 "direction"。

Ps(0).Direction = rdParamReturnValue
Ps(3).Direction = rdParamOutput
Ps(4).Direction = rdParamOutput

'设定输入参数值。
Ps.rdoParameters(1) = "Test%"
Ps.rdoParameters(2) = 1

'创建结果集并置入 Ps 值。
Set rs = Ps.OpenResultset(rdOpenStatic)

MyRetValue = Ps(0)   '返回值参数。
MyOutputVal1 = Ps(3) '第一个输出参数。
MyOutputVal2 = Ps(4) '第二个输出参数。

九. 创建 RDO 游标介绍用 RDO 创建游标的方法。
并非所有的查询都必须以游标的形式返回。事实上,游标是特别昂贵的获取数据方法,
很少在产品级的应用程序中使用。如果将 rdoDefaultCursorDriver 属性设置为
rdUseNone,那么应用程序创建的所有结果集的创建方式等价于:使用 OpenResultset
方法,设置了 rdOpenForwardOnly 和 rdConcurReadOnly 选项,并将 RowsetSize 设
为 1。这通常是将数据从远程服务器传递到应用程序的最有效的方法。
它也可以创建开销较低的结果集,并且能够通过 Edit/Update 方法进行更新。但是,
大多数情况下,这种方法不可行,因为基本表不能直接被更新,所以创建一个可更新的
游标是不可能的。无论什么时候用存储过程创建结果集,该结果集都不能被更新,至少
不能用 Edit/Update 方法更新。这时候,可以使用 WillUpdateRows 方法执行一个动
作查询,真正地完成更新。
无论怎样创建游标,通常都使用以下的各种方法来更新数据,即使该游标时不能被更新
的:
■ 执行一个存储过程,基于代码所提供的键更新选定的数据行。
■ 执行一个动作查询修改特别指定的数据行。在这种情况下,代码需要创建用于该查
询的适当的 WHERE 子句。
■ 使用 WillUpdateRows 事件来捕获更新操作,并且代之以适当的存储过程调用,来
执行更新。
 总的说来,很多情况下游标都不是访问数据的最优的或可行的方法,对于大型的产品
数据库尤其如此。但是,有些情况下,还是可以应用它的:
■ 在有限的结果集中向前或向后滚动(浏览)。
■ 根据保存的值(书签),移动到指定的数据行。
■ 移动到结果集的第“n”行,n 可以是绝对的位置,也可以是相对的偏移量。
■ 使用 RemoteData 控件,更新对基本表创建的有限的结果集。
 如果查询仅仅创建了基于一个或多个基本表的无限定游标,它的使用机会就更少了。
例如,"SELECT * FROM Table" 就是无限定查询的一个例子。这不仅在被保护的系统中
不允许使用,如果试图将应用程序用户扩展为多用户的,那么还会导致严重的并发错
误。在创建游标的时候,必须将其范围限制到尽可能少的数据行。在交互式系统中(有
操作人员参与),如果企图取得几百行以上的数据,往往会影响效率而且导致相当复杂
的并发性错误。
(一) 选择一个RDO游标库
RDO 支持几种不同的游标库,其中每一种都有其特定的作用。并非每个游标库都支持所
有类型的游标,但是所有的库都支持只能向前的结果集管理。例如,ODBC 客户端驱动
程序就只能支持 rdOpenStatic 和 rdOpenForwardOnly 游标类型,而 SQL Server 服
务器端的驱动程序可以支持所有的四种类型的游标。
在使用某些 RDO 功能时,正确地选择游标库是很重要的。例如,必须使用客户端的批
处理库或者 BatchUpdate 方法来创建分离的 rdoResultset 对象。要禁止游标的创
建,并使用只能向前的、只读的或单行的结果集,请使用 rdUseNone 选项。
在创建独立的 rdoQuery 对象的时候,如果该对象的查询作为方法使用,必须在查询执
行之前设置 CursorType,因为没有选项可以在执行查询时做这件事。
CursorType 属性值被用作 OpenResultset 方法的 type 参数。为了选择游标驱动程序
的类型,需要设置 rdoEngine 的 rdoDefaultCursorDriver 属性或者设置
rdoEnvironment 或 rdoConnection 对象的 CursorDriver 属性。下表描述了这些属性
的可用选项。
选项    描述

rdUseODBC  RDO 将使用 ODBC 游标库。这可以大大提高小型结果集的性能,但是结果
集越大,性能降低得越厉害。
rdUseServer  RDO 使用服务器端的游标,如果它们是可用的。
rdUseClientBatch RDO 使用客户端的批游标库。
rdUseIfNeeded  (缺省)ODBC驱动程序选择适当类型的游标,如果可用的话,使用服
务器端的游标。
rdUseNone  RDO 创建一个无游标的结果集。
使用 ODBC 游标库
ODBC 游标库是所有库中最简单的。它仅仅支持静态的和只能向前的游标,因此对于许
多开发者都具有不能满足要求的。对于创建几百行以上的游标来说,它也不是十分有效
的。但是,它是最具灵活性的,因为它可以有效地访问所有的 ODBC 驱动程序,即便是
那些不完全遵循 ODBC Level II 的驱动程序。
由 ODBC 游标库实现的静态游标只将选定的数据复制到工作站上而已。
注意   使用 ODBC 游标库时,不能执行仅仅返回一个 Binary Large Object (BLOB)
数据列的存储过程。但可以用 ODBC 游标驱动程序执行查询返回 BLOB 列和另外一个数
据列。Microsoft SQL Server 服务器端的游标库没有这种局限性。
使用 rdUseIfNeeded 游标库选项
CursorDriver 属性选项告诉 RDO:如果可行的话,选择服务器端的游标。因为它是缺
省选项,就意味者,如果连接到 Microsoft SQL Server 上,RDO 将选择服务器端的游
标。如果与 Oracle 或其它 ODBC 数据源相联,则使用 ODBC 游标库。
使用服务器端的游标库
如果数据源支持客户端的游标,而且选择了 rdUseServer CursorDriver 选项,那么远
程数据库引擎会在服务器上建立游标键集,并扩展 SQL 方言,以支持用游标创建和管
理数据。Microsoft SQL Server 6.0 及其更高级的版本支持服务器端的游标,但并非
所有数据源都支持。
服务器端的游标减少了对客户机上的内存和磁盘空间的需求量。但是负担转移到了服务
器上。对于 SQL Server 而言,游标键集保存在服务器的 TempDB 数据库中,因此,它
必须能够调整大小,以满足多个客户产生的附加的临时游标键集。
服务器端的事务 SQL 游标
另一种办法是:使用 SQL Server 自己的 Transact SQL 函数,实现服务器端的游标,
增强 SQL Server 的功能。通过这些函数,可以创建一个服务器端的游标,定位到指定
数据行,执行定位更新。'
服务器端的游标和多结果集查询
如果使用服务器端的游标库将包含多个 SELECT 语句的查询提交到 SQL Server,ODBC
驱动程序将拒绝该查询,因为它无法处理这种情况。如果必须执行多结果集的查询或包
含多个 SELECT 语句的存储过程,可以采取下面几种办法:
■ 将该查询转换成单独的 SELECT 语句提交。
■ 将 CursorDriver 属性设置为 rdUseNone,使用无游标的结果集。
■ 创建一个只能向前的、只读的 rdoResultset,其父对象 rdoQuery 的 RowsetSize
属性设置为 1。
■ 将 CursorDriver 属性设置为 rdUseODBC,转换到 ODBC 游标库。
使用客户批游标库
RDO 2.0 引入了 Client Batch 游标库。该库可以创建只能向前的结果集除外的所有类
型的游标。另外,在它的设计中使用了 Microsoft 专有的 Rushmore 技术,因此它比
ODBC 游标库要有效得多。用这个库也可以创建分离的 rdoResultset 对象,执行脱机
修改数据,将 rdoResultset 与一个活动的连接重新相联,从而将更新传回使用开放式
批更新的数据库。即便没有创建分离的 rdoResultset,仍然可以使用 BatchUpdate 方
法来提交数据库批更新。
Client Batch 游标库还可以创建客户端的所有游标。就是说,原始行的行映像和键集
都被置于客户系统中。
使用 rdUseNone 游标库
CursorDriver 属性告诉 RDO 取消游标的创建,只创建一个单行的、只能向前的、只读
的结果集。
并非所有的查询都需要以游标的形式返回。事实上,游标是取得数据的特别昂贵的一种
方法,在产品级的应用程序中极少使用它。如果将 rdoDefaultCursorDriver 属性设置
为 rdUseNone,那么应用程序创建的所有结果集都都相当于设置了如下选项:使用
OpenResultset 方法,设置了 rdOpenForwardOnly 和 rdConcurReadOnly 选项,并将
RowsetSize 设为 1。这是多数应用程序可用的最有效的结果集,因为它实现起来最简
单。但是它所具备的功能也是最少的,而这也可能是某些应用程序恰恰需要的。考虑到
这种方法的有效性,也许有必要让应用程序放弃豪华的游标。

(二) 选择RDO游标类型
rdoResultset 对象可以被定义为一系列的数据行,这些数据行可以通过游标访问也可
以不通过。rdoEnvironment 或 rdoConnection 对象的 CursorDriver 属性决定了使用
哪一种游标库,以及在哪里创建游标键集:在客户工作站还是服务器上。但是,
OpenConnection 方法的 type 参数决定了游标将如何被创建。
选择了一种 type 参数设置之后,返回的结果集可以为以下几种类型:
■ 只能向前的结果集。
■ 静态的可滚动的游标。
■ 键集游标。
■ 动态可滚动的游标。
 结果集类型是根据数据源的 ODBC 接口的可用类型和功能来选择的。但是,如果将
CursorDriver 属性设置为 rdUseNone 的话,RDO 将不会为 rdoResultset 创建游标,
它将永远(也仅仅)创建一个只能向前的、只读的、单行的、无游标的结果集。
只能向前的结果集
如果需要快速检索数据并且使开销最小,可以使用只能向前的 rdoResultset。这种类
型的结果集可以更新,但每次只显示一个数据行。它不创建游标键集,数据值也是静态
的,当数据源的数据被更新时它保持不变。但是它的效率高,所以,重建一个该类型的
rdoResultset 的速度可能快于建立并保持一个键集类型的 rdoResultset。
除非使用了 rdConcurReadOnly 选项并将 RowsetSize 设为 1,否则 RDO 将创建一个
游标来管理只能向前的结果集,这对性能会有影响。但是,如果选择 rdUseNone 作为
游标驱动程序的话,那么依据缺省值将创建一个只能向前的、只读的、无游标结果集。
静态的、可滚动的游标
除了可以更新这一点以外,静态的 rdoResultset 与 Jet 快照类型的 Recordset 对象
是类似的。在客户端或服务器端,该类型的游标按照所选的游标驱动程序创建数据行的
静态副本。静态游标数据看起来是不能修改的,就是说,在打开并完全置入到游标之
后,静态游标使用的结果集的成员资格、顺序和值通常都是固定的。如果其它用户对
(包括同一个应用程序中的其它游标)数据行进行更新、删除或插入,该游标都不会觉
察到,直至它关闭并重新打开为止。
键集游标
键集类型的 rdoResultset 与 Jet dynaset 型的 Recordset 对象类似。在游标中建立
并保存了键(实际上是书签),它存储在客户工作站或服务器中。在访问每一个数据行
时,所保存的键就用来从数据源取当前数据值。对于键集驱动的游标而言,一旦完全置
入到该键集之后,成员资格就固定了,在重建之前,影响成员资格的添加或更新操作不
会影响 rdoResultset。
在某些 ODBC 数据源上,使用 AddNew 和 Edit 方法直接对键集游标进行的修改和添加
都会包含到该结果集中。但是使用 Execute 方法的添加和修改却不会影响游标。与此
有关的详细信息,请参阅服务器手册。
要建立一个键集驱动的游标,客户和服务器必须有足够的资源,以保存键和一批(与
RowsetSize 有关)数据行。
使用 AddNew 方法时改变成员资格
在使用可更新的键集游标或静态游标时,虽然可以用 AddNew 方法在数据库中添加数据
行,但是,这个新行并不总是能够被添加到 rdoResultset 中,也就是说,有些游标库
将数据行同时添加到游标和基本表中,有些却不然。下表总结了各种游标库如何实现键
集游标和静态游标:
游标库      使用 AddNew 将数据行添加到游标中

rdUseIfNeeded (服务器端的) 能
rdUseODBC (ODBC)    不能
rdUseServer (服务器端的)  能
rdUseClientBatch(客户端批处理) 能
rdUseNone (无游标)   无法使用
动态的可滚动游标
除了成员资格不固定、不支持书签以外,动态类型的 rdoResultset 和键集驱动的游标
是一样的。因为 RDO 需要经常检查具有成员资格的所有的合格数据行,所以该类型游
标的系统开销是最大的。但是,动态类型的游标的启动可能会比键集游标要快,因为创
建键集游标时需要初始化键集。
通常情况下,要避免使用动态游标,因为它的实现和维护都是代价高昂的,也就是说,
该类型的游标会用掉大量的 RAM,并且要求相当的网络带宽。
使用 RowCount 属性
在首次创建一个键集类型的或静态类型的游标时,RowCount 属性被设置为:
■ 被置入的结果集的数据行数,如果返回了行。
■ 0,如果查询没有返回数据行。
■ -1,如果 RowCount 不可使用。
 当代码引用 RowCount 属性时,RDO 在将控制返回到应用程序之前完成 rdoResultset
的置入,对于数据行数可以改变的动态游标,以及只能显示一行数据的只能向前的结果
集,RowCount 是无效的。
操作的排序
如果有一个未完成置入的 rdoResultset 在数据源上挂起,而该数据源只能支持
rdoConnection 对象上的一种操作,那么,在 rdoResultset 被刷新、关闭或完成置入
之前,将不能创建另外的 rdoQuery 或 rdoResultset 对象,也不能对 rdoTable 对象
使用 Refresh 方法。例如,如果使用 SQL Server 4.2 作为数据源,或使用 SQL
Server 6.x 的客户端游标时,那么,在移到当前 rdoResultset 对象的最后一行之
前,将不能创建另外的 rdoResultset 对象。为了完成结果集的置入,可以使用
MoreResults 方法遍历所有挂起的结果集,或对 rdoResultset 使用 Cancel 或 Close
方法,以刷新所有挂起的结果集。有的数据源可能没有这种局限性。如果需要提交一个
查询,但是 hStmt 是无效的,那么必须打开另一个连接。
管理 RDO 的并发
在通过改变 OpenResultset 方法的 LockType 选项更改数据时,可以控制数据源和选
定的游标库如何管理行及页面的锁定。下表描述了四种类型的锁定。
LockType    选项 描述

rdConcurLock   保守的并发。这种方法使用了最低级别的锁定,充分确保了数据行的
更新。
rdConcurRowVer 使用行版本的开放式的并发。ODBC 游标库和数据源通过比较行的 ID
或 TIMESTAMP 的值来确定行是否改动过。
rdConcurValues  使用数据行值的开放式并发。ODBC 游标库和数据源对数据的值进行
比较。
rdConcurReadOnly  该游标是只读的。不允许任何形式的改动。
rdConcurBatch   使用客户端的批游标库,并且,在使用 BatchUpdate 方法之前,推
迟所有的更新。
注意   rdConcurLock 选项将锁定由 RowsetSize 属性设定的所有行集合。这种锁定在
结果集打开后立即创建,而不是等到执行 Edit 方法之后。因为行集合是要重定位的,
与该行集合有关的数据行和页将被锁定,其余的则被释放。由于这种机制,仅仅在特殊
情况下才使用保守的锁定。
开放式锁定
使用开放式锁定时,通过对比行数据值和行版本来检查并发。在这种情况下,将使用
Edit 和 AddNew 方法之前的原始行与通过 Update 方法更改之前的数据行进行对比。
如果这时行数据发生了改变的话,将导致一个可以捕获的错误。这时,临时的行缓冲区
将被丢失。要执行这些修改,必须使用 Move 0 方法刷新当前行,然后使用 Edit 或
AddNew 方法填入新值,并再次使用 Update 方法。

(三) 异步执行RDO
与 RDO 1.0 相比,RDO 2.0 支持更多的异步操作。例如,以下所有的操作都将允许使
用 rdAsyncEnable 选项:
■ 建立连接。在使用 OpenConnection 或 EstablishConnection 方法时,如果建立连
接所需的时间不能确定,该选项可以防止阻塞。
■ 创建结果集。在使用 OpenResultset 方法时,该选项可以防止查询执行的时候发生
阻塞。
■ 执行动作查询。在使用 Execute 方法时,该选项可以防止查询执行的时候发生阻
塞。
■ 置入到结果集。在使用 MoveLast 方法时,该选项可以防止在查询或置入到结果集
的时候发生阻塞。
■ 刷新结果集。如果在创建结果集的时候使用 rdAsyncEnable 选项,那么在用
Requery 方法刷新结果集时也可以使用它。
 为了更好地支持异步操作,RDO 支持以下两种属性,它们可以用来轮询异步操作的完
成情况:
■ StillExecuting 属性测试使用 OpenResultset、Execute 或 Requery 方法的查询
是否完成。
■ StillConnecting 属性测试使用 OpenConnection 或 EstablishConnection 方法的
连接尝试是否完成。
 另外,RDO 还显露了 rdoConnection 对象的一些事件,它们在操作完成时被触发。
■ Connect 事件在连接尝试完成后被触发,无论连接成功还是失败。
■ QueryComplete 事件在查询或重定位 (MoveLast) 操作完成时被触发。
处理异步查询
应用程序将查询提交到远程服务器之后,通常无法确定需要多长时间才能返回第一行数
据。由于 SQL Server 和其它服务器处理结果集的方式,应用程序在满足下列条件之前
将无法得到结果:
■ 所有行都已被处理,例如排序过的结果集。
■ 服务器端的行缓冲区已满(大约 4K)。
■ 查询完成,而且服务器端的行缓冲区已被刷新。
 一个查询需要的时间可能是几个毫秒,也可能多达到数小时,如果在服务器完成工作
之前服务器或工作站资源被耗尽,那么它将永远也不能完成。有时远程服务器相当迅速
地开始将数据行传回,同时 RDO 开始根据选定的游标类型置入到 rdoResultset 。但
是,远程服务器经常会停止工作,等待应用程序向前移动游标,访问数据行。在很多情
况下,数行或整页的数据被锁定在服务器上,直到查询完成,这只有置入了
rdoResultset 的最后一行之后,这才可能发生。为此,需要使用一种 Move 方法,如
MoveLast,或者引用 RowCount 属性。
防止应用程序阻塞
由于这种不确定性,在执行查询的时候,有必要预防应用程序被阻塞,并确保应用程序
尽快完成结果集的置入。除非设置 OpenResultset 方法的 rdAsyncEnable 选项使异步
操作有效,否则,在 ODBC 驱动程序返回结果集的第一行数据之前,应用程序将被阻
塞。
除了设置 rdAsyncEnable 选项以外,还可以编写一个事件处理程序,用来捕获查询完
成时发生的 QueryComplete 事件。
如果使用 OpenResultset 方法的 rdAsyncEnable 选项,RDO 将立即把控制返回到应用
程序。这时,rdoResultset 对象被实例化了,但其行集合却还不能付诸使用,需要等
到远程服务器开始传回数据行。为了表明状态的变化,在第一行返回之前,
StillExecuting 属性将返回 True。一旦 StillExecuting 返回 False,rdoResultset
数据就是可用的,并触发 QueryComplete 事件。
等待异步操作时做些什么
使用了异步操作选项之后,当远程服务器忙于执行查询或置入到结果集时,应用程序会
得到控制权。因为该操作可能要化费一定的时间才能完成,所以必须确定在这一段时间
内应用程序需要做些什么。下面的建议将有助于选择可行的策略:
■ 建立一个事件处理程序,用来捕获 Connect 事件。这个事件处理程序可以用于最终
确定连接操作,或仅仅测试连接的有效性。例如,在该处理程序中,可能提交应用程序
专有的一组注册或初始化过程。
■ 建立一个事件处理程序,用来捕获 QueryComplete 事件。当 RDO 检测到第一个结
果集已经就绪,可以处理,或者查询失败时,该事件被触发。这种方法比轮询
StillExecuting 属性要好。
■ 如果不能创建一个事件处理程序来通知应用程序异步操作已经完成,还可以周期性
地轮询 StillExecuting 或 StillConnecting 属性来测试该操作是否完成。这个循环
必须包括 DoEvents 语句,从而允许应用程序的其它部分能够访问处理器(除非由于其
它设计因素不能这样做)。
■ 假定前一个操作已经完成,可以考虑使工作站的处理器运行交替的过程。例如,假
设应用程序正在处理来自外部文件的数据行,并用 INSERT 语句将它们传递到远程服务
器上,这时就可以采用这样的办法。当远程服务器处理一批 INSERT 语句时,应用程序
可以准备要传送的下一批数据。一旦第一批完成,随后的一批已经准备好了,就可以这
样循环下去。
 下图说明了如何建立异步文件导入例程。
图 11.2  在等待异步操作时,运行交替的过程
 外部数据





      否

        是    (DoEvents)

这种方法既推动了在远程服务器上执行查询的线程,也加速了工作站上的有效线程。
等待异步 RDO 操作时不能做什么
只要异步操作还在执行,那么除了下表所列的属性和方法之外,不能再去引用正在被处
理的 rdoResultset, rdoQuery 或 rdoConnection 等对象。
在异步操作期间有效的属性和方法
远程数据对象    有效的属性和方法

rdoConnection   StillConnecting 属性;Cancel, Close 方法
rdoQuery    StillExecuting 属性;Cancel, Close 方法
rdoResultset   StillExecuting 属性;Cancel, Close 方法

如果确认等待一个异步操作的时间太长,可以用 Cancel 或 Close 方法通知远程处理
器放弃该操作。
■ Cancel 方法将放弃该查询操作。
■ Close 方法放弃该操作,并废弃它所应用于的 RDO 对象。
 在提交了一个复杂的多语句的查询,或大范围的更新操作时,使用这两种方法都是不
太谨慎的,因为数据库的完整性可能会遭到破坏。

(四) 编写RDO事件处理程序
RDO 2.0 提供了很多事件,利用这些事件可以使异步操作处理、集中式错误处理和公共
例程的实现更加简单。这些事件过程都在初始的 RDO 对象创建之后即成为有效的。在
Dim 语句中使用 WithEvents 时,代码中会自动显露对象的事件处理程序。例如,为了
在 rdoConnection 对象中显露这些事件,可以用下面的编码:
Private WithEvents MyCn As rdoConnection
 在设计模式中,代码窗口中的 Object 下拉列表中会包含 MyCn 一项。一旦选择了
它,每个 rdoConnection 事件都会被显露。
尽管不能使用 Dim x As New 语法使用 WithEvents 运算符,可以使用以下语法来实例
化一个独立的 RDO 对象。
Private WithEvents MyCn As rdoConnection
...
Set MyCn As New rdoConnection

请注意,rdoQuery 对象没有显露任何的事件,它们都显露在父对象 rdoConnection
 中。rdoQuery 对象被捕获的每一个事件的rdoConnection 处理程序都传递了一个指
针,指向触发该事件的查询。例如,rdoConnection 对象的 WillExecute 事件可能写
成下面的样子,表明将作为查询提交的 SQL 语句。
Private Sub MyCn_WillExecute( _
 ByVal Query As RDO.rdoQuery, Cancel As Boolean)
Msgbox "About to exectue:" & Query.SQL
End Sub
详细信息   关于使用 WithEvents 关键字的详细信息,请参阅《程序员指南》中“用
对象编程”的“添加事件到类”一节,以及《联机手册》中的《语言参考》的有关内
容。

(五) 使用RDO事件
也可以采用循环轮询以外的方式测试异步操作是否完成,即编写自己的事件处理程序,
在查询、连接或其它异步操作的事件发生时将其捕获。
下表对每一种 RDO 异步查询事件进行了简单的描述。
rdoEngine 对象的异步事件

事件   触发于...

InfoMessage ODBC API 函数返回 SQL_SUCCESS_WITH_INFO 时。

rdoEnvironment 对象的异步事件

事件   触发于...

BeginTrans BeginTrans 方法完成之后。
CommitTrans CommitTrans 方法完成之后。
RollbackTrans RollbackTrans 方法完成之后。

rdoConnection 对象的异步事件

事件    触发于...

BeforeConnect  在调用 SQLDriverConnect 之前,从而允许开发人员进行提示。
Connect   连接操作完成之后,无论是否成功。
Disconnect  连接关闭之后。
QueryComplete  查询完成之后,无论是否成功。
QueryTimeout  QueryTimeout 所设置的时间已用完,而查询仍未完成。
WillExecute  在查询执行之前,从而允许开发人员阻止查询的执行,或在最后时刻对
SQL 作些调整。

rdoResultset 对象的异步事件

事件     触发于...

Associate    在一个新的连接被关联到该对象之后。
Dissociate    与连接分离之后。
ResultsChange   当前的行集合被修改之后(多结果集)。
RowCurrencyChange 当前行的状态改变(由于编辑、删除或插入等操作)之后,或当前
行指针改变时。
RowStatusChange  当前行的状态改变(由于编辑、删除或插入等操作)之后。
WillAssociate   一个新的连接被关联到一个对象之前,开发人员可以影响或取消该操
作。
WillDissociate   在与连接分离之前,开发人员可以影响或取消该操作。
WillUpdateRows  在对服务器的更新发生之前,开发人员可以影响或取消该操作。

rdoColumn 对象的异步事件

事件    触发于...

DataChange  当数据行的值发生改变时。
WillChangeData 在数据行的值改变之前,允许开发人员取消它。

十. 处理 RDO 结果集如何处理查询返回的数据和参数。
在创建了 rdoResultset 之后,就可以访问其中的数据了。在处理结果集时,需要注意
以下几点:
■ 如果该结果集以游标的形式创建,必须尽快置入到它,因为游标所取得的数据行和
页可能被远程数据库引擎锁定。直到定位到最后一行时该结果集才算完成置入。
■ 在很多情况下,无游标的结果集更为有效。rdoResultset 对象的功能以满足需求为
宜,不要具有多余的特性。例如,如果并不需要使用游标修改数据,就不要请求允许更
新的结果集。如果仅仅要置入到一个列表框,可以任意滚动的游标也是不必要的。
■ 考虑使用无游标的结果集和动作查询来执行更新。在很多情况下,这种方法能够提
高性能。尽管实现起来更复杂一些,在不能直接访问基本数据表时,可以采用这种方
法。
■ 尽可能地使用异步操作和事件过程,以避免应用程序被锁死,至少能够减少这种可
能性。也可以异步处理 Move 方法,例如使用 MoveLast,防止在置入到结果集时被锁
死。
■ 为复杂的结果集处理创建几个小的结果集,这常常会使速度更快,而且可以更好地
管理系统资源。
■ 尽可能从存储过程产生结果集,因为这样可以提高服务器、网络和工作站的效率,
并使应用程序的开发更为简单。
■ 从连接上断开已有的、以客户端的批处理游标的形式创建的 rdoResultset 对象。
可以继续使用 Edit 或 AddNew 方法修改数据。当准备将所作修改传回数据库时,可以
将其 ActiveConnection 对象设置为一个打开的 rdoConnection 对象,从而将
rdoResultset 关联到一个打开的连接上。
设置 RDO 游标限制
创建一个 rdoQuery,并设置其 MaxRows 属性,即可限制查询返回的数据行数。一旦查
询处理程序返回了 MaxRows 行数据之后,便不再执行查询了。这是控制从查询返回的
行数的最简便的方法。
注意   MaxRows 属性也会影响到数据修改查询。例如,如果 MaxRows 设为 100,然后
执行一个动作查询,例如 UPDATE 语句,那么只有开始的 100 个数据行会被更新。
确定返回了多少行数据
通常,可以通过 RowCount 属性来确定结果集成员的数目。在访问该属性时,RDO 基本
上是在返回到应用程序之前执行 MoveLast 方法,从而完全置入到结果集。这种操作不
是异步执行的,所以即使查询完成了,应用程序还是可能被阻塞。有的 ODBC 不支持
RowCount,那么该属性将返回 -1。
处理 RDO QueryTimeout 事件
如果要限定查询处理程序处理某个查询的总时间,可以设置 rdoConnection 或
rdoQuery 的 QueryTimeout 属性。一旦超过 QueryTimeout 的期限,就会触发
QueryTimeout 事件。只要过了 QueryTimeout 的时间就会被触发该事件,无论是否使
用了 rdAsyncEnable 选项。
事件处理程序可以选择继续执行异步查询操作还是取消。如果开发者或用户决定继续执
行,再等待一个 QueryTimeout 周期,请传递 False 到 Cancel 参数。对
QueryTimeout 属性所作的修改要等到下一个查询开始时才有效。
如果没有编写 QueryTimeout 事件过程,查询会被取消,StillExecuting 属性将设为
False,并将控制返回应用程序。
(一) 定位当前行指针
在任何时候,rdoResultset 集合中只有一行数据可以用于获取数据和修改,即当前行
指针所指的那一行。通过使用下表中所列的方法或属性之一,可以在数据行之间移动当
前行指针。这里的每一种方法都暗示了一个起始位置,重定位是相对这个起始位置而言
的。有的时候当前行指针可能是无法确定的,或定位越界。这种情况下就不存在当前
行,因此试图引用或更新数据列都是不允许的。有的时候(例如只能向前的结果集)不
允许向后移动结果集,甚至不能通过使用 MoveLast 方法移到结果集的尾部。通常这是
由所选择的游标类型决定的。
重定位当前行指针的方法和属性

RDO 方法和属性    移至 rdoResultset 对象的... 相对于 rdoResultset 的 ...

MoveFirst     第一行     任何地方
MoveNext     当前行 + 1    当前行
MovePrevious    当前行 – 1    当前行
MoveLast     最后一行     任何地方
Move     向前或向后移动 'n' 行  当前行
PercentPosition    被置入的数据行的百分比  第一行
AbsolutePosition   第 'n' 行     第一行
Bookmark     预先保存的位置    任何地方
LastModified    RDO 设置的预先保存的位置 任何地方
 提示  使用无参数的 Move 方法可以简单地重新取得当前数据行。
注意   如果 BOF 或 EOF 属性为 True,则不能使用书签来重定位当前行指针。如果要
处理一个可以滚动的 rdoResultset,并且希望重定位到一个书签,请使用 MoveFirst
方法或其它的 Move 方法来重定位到结果集。

如果该 rdoResultset 对象支持书签,那么可以将当前位置保存到一个 Variant 变量
中。将 Bookmark 属性设置为一个书签,以后就能够用保存的书签重新回到
rdoResultset 中原来的位置。LastModified 也返回一个书签,可以将其保存在
Bookmark 属性中,用于重定位到最后一次修改或添加的那一行。
注意   如果使用的是只能向前的 rdoResultset 对象,那么只能使用MoveNext 方法。
其它的重定位方法都是不支持的。但是,在任何时候都可以用 Close 方法关闭该结果
集,或用 MoreResults 方法中止当前 rdoResultset 对象的处理。
 一定要检查 EOF 和 BOF 属性,以确定重定位的当前数据行是否超出了数据集的范
围。在使用 Bookmark 属性重定位当前行时,可能会定位到已经被其它用户删去的数据
行,这时将导致一个可以捕获的错误,这时必须重定位到另一个有效的数据行。
详细信息   请参阅《联机手册》的《语言参考》的“BOF 属性”、“EOF 属性”、
“AbsolutePosition 属性”、“PercentPosition 属性”和“移动”部分。
启用异步的 RDO MoveLast 方法
只要可能,在完全置入到结果集之后必须立即执行 MoveLast 方法。当 MoveLast 定位
到该结果集的最后一行之后,远程服务器将释放语句句柄、所有的页和行锁定。
在使用 MoveLast 时如果设置了 rdAsyncEnable 选项,RDO 将立即把控制返回到应用
程序,从而防止不必要的锁死。这种情况下,在 StillExecuting 属性返回 False 之
前,rdoResultset 数据是不可使用的。这时也会触发 QueryComplete 事件。

(二) 更新RDO数据
并非所有的 rdoResultset 对象都可以被更新。有时候,不能使用游标来更新一个结果
集,但可以使用动作查询来。为了用游标更新基本表,必须满足下面的条件:
■ 该表具有唯一的索引,游标库可以使用它来唯一地标识数据行。如果发现一个表无
法被更新,最常见的原因是没有唯一的键。
■ 已登录的用户对于基本的数据列、表和数据库具有读写访问权。在有的情况下,更
新表的唯一途径是通过存储过程。
■ 远程系统的数据库管理员允许对表进行直接更新(多数不允许)。
■ 结果集基于一个复杂的多表联结。虽然可以更新某些联结,但并非所有的联结都是
可更新的。
■ 游标类型支持更新。
■ 其它用户此时没有访问这些数据。其它用户也许将数据页锁定了?
■ rdoResultset 和 rdoColumn 的 Updatable 属性均返回 True。即使属性为 True,
结果集或者一些特殊的数据列仍然可能是不可更新的。
通过存储过程进行更新
多数多用户系统中的应用程序不会试图直接对基本表进行更新。创建一个游标来更新选
定的表的指定列,这看起来很简单,但系统管理员可能会禁止这种极其危险的操作。另
外,数据库业务规则和关系的完整性经常依赖于逻辑操作中的相关表同步。数据库可能
包含触发器或其它的机制,用来自动执行这些操作的一部分或者全部,因此应用程序就
可以更简单,不必处理这些细节。
为了更新一个基本表,并使用 RDO 游标来执行一些辅助操作,可以在应用程序中开始
一个事务,并管理更新操作的各个方面。这可以通过执行若干独立的更新操作来实现。
但是,对于需要更新表的每一个应用程序,都需要复制这种相当复杂的方法。一旦业务
规则或引用的完整性模式有所改变,应用程序就必须被重新编译并重新分发。
为了实现一种更好的解决方法,大多数情况下都使用存储过程来返回行结果集,并更新
指定的数据行。这种方法增强了绝大多数的远程数据库和网络的安全机制,并且已经成
功地实现于支持 1,000 或更多用户的系统中。该方法的另一个优点是高性能。因为应
用程序通常是为了处理范围有限的结果集,并且不仅仅依赖于游标,所以,工作站、网
络和远程服务器将更有效进行合作。
通过 WillUpdateRows 执行存储过程实现更新
RDO 2.0 允许在使用游标更新数据库时执行存储过程。这就意味着可以用自己的存储过
程来添加、修改、删除数据。如果为 WillUpdateRows 事件编写了一个过程,那么可以
提交一个存储过程来执行更新操作。或者只编写一些更新或其它的操作来代替基于游标
的操作,或者作为附加的操作。
如果结果集不是批处理模式,那么在 ODBC 更新被送到服务器之前,每次调用 Update
方法都会触发 WillUpdateRows 事件。如果是批处理模式,则只有在 BatchUpdate 方
法执行时才触发 WillUpdateRows 事件。
通过检查 WillUpdateRows 的 ReturnCode 参数,可以知道更新操作的成功或失败。下
表总结了可以用作 ReturnCode 参数值的选项。
ReturnCode   表明

rdUpdateSuccessful 更新操作成功。RDO 不通知其它处理程序(如果该事件有多个的
处理程序),并且将列标记为更新的。
rdUpdateWithCollisions 更新操作成功,但有些行产生了冲突(仅对批处理模式而
言)。RDO 不通知其它处理程序(如果该事件有多个处理程序),也不标记更新过的
列。
rdUpdateFailed 更新操作不成功。RDO 不通知其它处理程序(如果该事件有多个处理
程序),并且触发一个可以捕获的运行时错误,不标记更新过的列。
rdUpdateNotHandled (缺省)应用程序并没有处理更新操作。RDO 继续通知其它处理
程序(如果有的话)。如果没有其它过程执行更新,RDO 将更新数据并标记更新过的
列。
使用游标更新数据源
如果数据库没有实现严密的引用完整性或复杂的业务规则,允许直接访问基本表,那么
可以使用 rdoResultset 游标来更新数据行。注意,许多关于更新的问题都与并发管理
有关,例如数据行和页的锁定,共享数据等。错误处理程序必须准备处理各种可能发生
的情况,包括行和页锁定冲突、丢失连接、更新冲突以及其它许多可能性。
修改 rdoResultset 中数据行的值包括以下步骤:
1. 使用“定位当前的行指针”一节中讨论的重定位当前行指针方法之一进行定位。
2. 使用 Edit 方法激活要修改的行。所有数据都被复制到一个临时的行缓冲区中。
3. 为需要修改的数据列提供新值。要使用临时行缓冲区,只需要使用 rdoResultset
对象的列,就象从数据列中检索数据时那样。
4. 所有的数据列都修改为正确值之后,使用 Update 方法将行缓冲区保存到数据源。
当前行被删除,缓冲区中的数据行取代了它。这时,LastModified 属性将返回被修改
行的书签。
放弃编辑
在使用 Update 方法之前,并没有进行任何真正的修改,直到执行该方法时才将所进行
的编辑提交到数据库。但是,如果决定放弃所作的编辑并返回当前行的未经编辑的版
本,可以有以下这些选择:
■ 将当前行指针移到另一行。
■ 使用无参数的 Move 方法刷新当前行。
■ 使用 CancelUpdate 方法。
■ 使用 Rollback 方法撤销该事务,假定前面使用了 BeginTrans 方法。
■ 在 WillUpdate 事件中将 Cancel 参数设置为 True,从而避开 ODBC 的实际更新操
作。
 编辑过后,当前行指针指向修改过的行,可能会定位在 rdoResultset 的末尾。要重
新访问刚刚被修改过的数据行,可以使用 LastModified 属性提供的书签。
添加及删除 rdoResultset 数据行
除了修改指定数据行的列之外,还可以添加一行数据到 rdoResultset 中,即添加一行
数据到基本表中。该操作假定应用程序具有读写访问权限,并允许添加数据行到基本
表。因为情况常常不是这样的,所以必须详细了解 WillUpdate 事件,以及本章中关于
使用存储过程的内容。
使用以下步骤添加指定的数据行到 rdoResultset:
1. 使用 AddNew 方法创建一个临时的行缓冲区,用来保留待添加的新行。
2. 为需要修改的每一列提供新值。要使用临时行缓冲区,只需要使用 rdoResultset
对象的列,就象从数据列中检索数据时那样。
3. 使用 Update 方法将数据行保存到数据源。
 添加完成后,如果游标支持对新的数据行的访问,那么当前行指针指向添加新行之前
的当前行。在这种情况下,可以通过 LastModified 书签将指针移动到位于
rdoResultset 尾端的新添加的数据行。
添加了新的数据行之后,如果具有删除数据的权限(可能没有),可以从结果集中删除
它。
使用以下步骤从 rdoResultset 中删除指定的数据行:
1. 用某种 Move 方法、AbsolutePosition 或 PercentPosition 属性定位到该行。
2. 使用 Delete 方法从游标(如果有)及数据源中删除该行。
 删除完成后,当前行不再是有效的,因此必须重定位到 rdoResultset 的另一个有效
行。
通过 RDO 动作查询进行修改
比 Edit、AddNew、Delete 和 Update 方法更有效的是使用 Execute 方法。执行包含
一个或多个 UPDATE、INSERT、DELETE 语句的 SQL 查询,也可以对数据库进行修改。
根据数据源的类型及支持复杂的多语句操作的能力,这些 SQL 可以执行所谓的“生成
表”或 SELECT INTO 查询,查询用于创建临时或永久的新数据表,也可以执行其它的
复杂操作。必须亲自管理错误和并发。也可以使用数据源支持的 SQL 语法提交事务语
句,将若干个操作集中到一个或多个原子集合中。因为 Execute 方法也支持
rdAsyncEnable 选项,所以这些操作也可以异步执行。
注意   Execute 方法不是为返回数据行的查询而设计的。如果执行的存储过程既有
“动作”操作,又返回数据行的操作,那么必须使用 OpenResultset 方法,分析产生
的结果集。

(三) 处理多RDO结果集
任何 SQL 语句都可能包括多个 SELECT 语句或存储过程,后者又可能调用一个或多个
SELECT 语句。每一个 SELECT 语句产生一个结果集,它必须由代码进行处理,或者在
RDO 资源被释放,并且下一个结果集成为可用的之前被放弃。
动作查询也会产生无数据行的结果集,这也是必须处理的,因为这是多结果集查询的另
一种类型。在很多时候,执行一个存储过程可能会返回多个结果集。存储过程是否会返
回多个结果集往往很难确定,因为存储过程可能调用了另一个过程。
例如,假设提交了一个查询,其中包括四个 SELECT 查询,用来置入到四个本地的
ListBox 控件,还包括一个事件过程,用来更新表,那么代码至少需要处理五个结果
集。因为不知道会存储过程会产生多少个结果集,因此代码也就必须准备处理 n 个结
果集。
执行多结果集的查询有两种方法:
■ 对一个连接直接执行 OpenResultset 方法。
■ 使用 rdoQuery 的 OpenResultset 方法。
 这两种方法处理都很相似,但如果使用 rdoQuery 的话,可以检查 RowsAffected 属
性以确定动作查询影响的数据行数。虽然也可以使用 Execute 方法执行多结果集查
询,但却不能获取被单个语句影响的那些数据行,如果有的查询返回了数据行,将导致
一个可以捕获的错误。
使用服务器端的游标库处理多结果集
并非所有的游标驱动程序都支持对多结果集查询的处理。SQL Server 服务器端的游标
驱动程序就是这样的一个例子。但是,如果使用 rdOpenForwardOnly、
rdConcurReadOnly 选项,并将 RowsetSize 属性设为 1,即可请求建立一个无游标结
果集,这样就可以使用服务器端的游标来执行多结果集查询。将 CursorDriver 属性设
为 rdUseNone 也可为所有结果集设置这些选项。
多结果集:一个示例
本节将分步讲解使用 rdoQuery 对象来执行多结果集查询的步骤。
提示   在使用连接符 "&" 建立 SQL 查询时,运算符之间一定要有空格(可以用空格
键或 TAB 键)。

1. 创建 SQL 语句,并将其放到一个字符串变量中,如 MySQL。对于 SQL Server,多
个语句之间必须用分号分开。
 Dim MySQL As String
 MySQL = "Select Name from Authors Where ID = 5; " _
  & " Select City from Publishers; " _
  & " Update MyTable " _
  & " Set Age = 18 Where Name = 'Fred'"

2. 然后,创建一个新的 rdoQuery ,并设置一个声明为 rdoQuery 类和多结果集的变
量到该对象,在这个例子中是 MyQy。本例假定 rdoConnection 对象 (Cn) 已经存在。
有许多方法可以实例化和初始化 rdoQuery 对象,这里只介绍了其中的一种。
 Dim MyQy As rdoQuery
 Set MyQy = Cn.CreateQuery("MyQy1", "")
 MyQy.SQL = MySQL

3. 对 rdoQuery 对象使用 OpenResultset 方法执行该查询。如果不需要将特别的属性
和功能传递到该查询,那么可以直接使用 rdoConnection 对象的 OpenResultset 方
法。所用的参数会影响到查询产生的全部结果集。例如,如果需要在第二个结果集中使
用游标,则必须在断开第一个结果集之后指定游标类型。
 Dim MyRs As rdoResultset
 Set MyRs = MyQy.OpenResultset(rdOpenForwardOnly, _
   rdConcurReadOnly)

4. 现在已经准备好了,可以处理第一个结果集了。注意,rdAsyncEnable 选项参数还
没有设置。因此,在第一个结果集的第一行准备好之前,控制不会返回到应用程序。如
果当前的 rdoResultset 包含数据行,RowCount 属性将被设为一个大于零的值,EOF
和 BOF 属性都是 False。如果未返回数据行,则 RowCount 属性返回 -1 或 0,表明
数据行的数目无效,返回 -1 还是 0 由驱动程序和数据源决定。
 下述例子将查询结果填入名为 NameList1 的列表框控件中。
 While Not MyRs.EOF   '循环执行所有的行。
         '使用第一列。
  NameList1.AddItem = MyRs(0)
  MyRs.MoveNext   '定位到结果集的下一行。
 Wend

5. 第一个结果集已到达文件结尾处 (MyRs.EOF = True)。使用 MoreResults 方法激活
下一个。一旦执行了 MoreResults 之后,第一个结果集便不再可用了,即使用游标选
项来创建它。
 '激活下一个结果集。
 If (MyRs.MoreResults) Then ...

6. 现在已经准备好处理第二个结果集了。本例只使用了开始的几个名称,并废弃了剩
余的数据行。
 '循环若干行。
 Do While Not MyRs.EOF and MyRs(0) < "B"
  '使用第一列。
  NameList1.AddItem = MyRs(0)
  MyRs.MoveNext
 Loop
 '激活下一个结果集,
 '并废弃剩余的数据行。
 If (MyRs.MoreResults) Then ...

7. 现在已经准备好处理最后一个结果集。因为这是一个 UPDATE 语句,没有返回的数
据行,但可以通过 RowsAffected 属性确定受到影响的数据行的数目。最后,使用
MoreResults 方法释放与该查询相联的所有资源。
 If MyQy.RowsAffected = 0 Then
   MsgBox "No rows were updated"
 End If
 '激活下一个结果集。
 If (MyRs.MoreResults) Then ...

对最后一个结果集使用 MoreResults 方法之后,将返回 False,执行该查询所要求的
其它资源也会被释放。这时,rdoQuery 对象可以再次使用。如果对 rdoQuery 使用了
Close 方法,它将从 rdoQueries 集合中删除。

(四) 使用GetClipString方法
有的时候需要用 Clip 属性来填充 Grid 控件,或其它支持成批加载的控件。可以使用
GetClipString 方法返回一个分隔字符串,包括结果集的 'n' 行。然后该串被应用到
目标控件的 Clip 属性,该属性将数据从结果集中置入到目标控件。
按照 NumRows 参数,GetClipString 方法返回结果集的 'n' 个数据行组成的串。如果
要求返回的数据行多于有效的行数,那么只返回有效的行数。通过 RowCount 属性可以
确定实际取到的行数。可以取到的数据行数要受到可用内存的限制,还要适合相应的应
用程序。如果表或结果集很庞大,那么不要一次把它们都装进内存。
通常,GetClipString 方法类似于 GetRows,只是它返回的不是一个二维变体型数组,
而是一个字符串。GetClipString 可用于填充一个 grid 控件或其它具有 Clip 属性的
控件。将数据从结果集导出到顺序文件时,可以用它来进行格式化。
完成 GetClipString 调用后,当前行指针指向下一个未读过的行。这与 Move(行数)
方法是等价的。
如果要多次调用 GetClipString,取得所有的数据行,可以通过 EOF 属性确定是否还
存在有效行。如果 GetClipString 返回的行数少于所要求的行数,要么是因为已经到
达 rdoResultset 尾部,要么是因为在要求的范围内不能再取得数据。例如,假如要取
得十行,但是第五行是取不到的,那么 GetClipString 将返回四行数据,并把当前指
针指向出现问题的那一行。这时不会产生运行时错误。
ColumnDelimiter 任选参数可以定义一个列分隔符,缺省的符号是 tab (Chr$(9);
RowDelimiter 任选参数可以定义一个行分隔符。在使用接收剪贴、但是分隔符不同的
控件时,这是非常有用的(某些 grid 需要一个回车符和一个换行符作为行分隔符)。

十一. 执行开放式的批更新如何使用开放式的批更新特性。
RDO 2.0 用一种新的技术来成批提交多个数据库操作。ODBC 和 SQL Server 服务器端
的游标库都支持对支持此概念的数据源的多语句批处理。但是,该操作不由游标驱动程
序管理,因此必须人工处理错误和各种偶然的情况。与之相反,Client Batch 游标库
是自动的:
■ 跟踪它所创建的 rdoResultset 对象中每一行的状态。当某一行被修改、删除,或
添加了新的一行,Status 属性会自动设置,以反映该数据行是修改过的。通过检查数
据行的状态,Client Batch 游标库可以跟踪那些被添加、修改、删除,或没有改动过
的数据行。
■ 管理独立的 rdoResultset 对象。尽管 rdoResultset 不能用保存的本地数据独立
地创建,在创建了静态结果集之后,可以将其从远程服务器上断开,然后对
rdoResultset 对象进行任意的修改。通过再次设置 ActiveConnection 属性,可以将
rdoResultset 重新联到另一个连接上,并使用 BatchUpdate 方法传递所作的修改。
■ 允许推迟更新操作。因为 Client Batch 游标库被设计为对数据行的集合进行批处
理操作,所以只要需要,可以推迟将所作的修改传递到数据库。
■ 建立执行数据库更新所需的 SQL 语句。Client Batch 游标库会在每一个 SQL 语句
之后添加一个适当的 WHERE 子句,通过关键字准确地标识指定行。
■ 管理每次操作的结果,并向应用程序报告操作是否成功。
 一般地,在采用延迟更新策略时,通常假定在脱机或延迟操作时不会有重大的修改。
如果是另一种情况,数据经常被查询并修改,那么这种策略很快就会崩溃。Client
Batch 游标库可以捕获预料不到的更新冲突,例如,另一个用户修改的记录是正在被更
新的结果集中的一列。在发生这种情况时,与冲突相关的每一行都会作上标记,而且,
这些应用程序可以使用数据的三个副本:
■ 应用程序第一次从数据库中读入的数据。
■ 数据库中记录的数据(其它用户已经修改过)。
■ 用户试图保存的数据。
 如何使用这三个版本的数据,是应用程序需要解决的问题。

(一) 激活RDO Client Batch游标库
如果应用程序需要使用 RDO Client Batch 游标库所提供的功能,必须通过以下方法将
它选为用于操作的游标库:将 rdoEngine 对象的 rdoDefaultCursorDriver 属性或
rdoEnvironment 对象的 CursorDriver 属性设置为 rdUseClientBatch。Client Batch
游标库可以创建和管理任何类型的游标,不过,它要受到 ODBC 驱动程序的限制。

(二) 创建并更改RDO客户端批处理
如果将 CursorDriver 设置为 rdUseClientBatch,那么可以使用 Client Batch 游标
库进行延迟或不延迟的批更新操作。为了选择理想的更新操作类型,请按照如下方式设
置 OpenResultset 方法的 LockType 属性:
■ rdConcurBatch 用来选择有延迟的更新。在这种情况下,在执行 BatchUpdate 方法
之前,Update 方法仅仅将修改传递到当前的 rdoResultset,而没有对基本数据库表进
行修改。
■ 其它所有并发选项。在这种情况下,Update 方法将立即对数据库进行逐行更新。
将 rdoResultset 从它的连接上断开
当 rdoResultset 被打开之后,可以立即开始修改。在完全置入到 rdConcurBatch 类
型结果集之后,可以立即设置 ActiveConnection 属性为 Nothing,从而将
rdoResultset 从当前连接上断开。这时,可以对数据行进行添加、删除或其它更新操
作。
修改结果集
要进行这样的修改,需要在使用 Edit、Delete 和 AddNew 方法之后执行 Update ,提
交所作的修改。在所有修改全部完成之后,使用 BatchUpdate 方法将这批更新传递到
数据库。BatchUpdate 方法对该 rdoResultset 进行扫描,检查每一行的 Status 属性
(所有未经修改的数据行状态都被设为 rdRowUnmodified )。如果该行修改过,也就
是说 Status 没有设为 rdRowUnmodified,Client Batch 游标库将产生一个 SQL 查询
来执行这些修改。该查询与用于本地结果集和远程服务器数据同步的其它 SQL 操作连
接在一起。一旦所有的操作都包含进来,批更新开始执行(传递到远程服务器上去执
行)。
设置 UpdateOperation 属性
UpdateOperation 属性决定了 RDO 和客户端的批处理游标如何对数据进行更新操作。
一般来说,有两种办法:一步完成更新,或者特意将其分成二个独立的操作,先进行删
除,然后插入。
注意   这与数据库触发器或参照完整性检查有关,可能无法执行单独的删除操作。
 在很多情况下,更新操作都是由数据库引擎在内部处理的,它的处理方式为:先执行
删除操作,然后立即执行对应的插入操作,两个操作被合并为一个原子事务。除非数据
库引擎支持“现场更新”,而且更新操作满足进行这种更新的条件,否则,更新操作将
以先删除、后插入的方式执行。
UpdateOperation 属性可以设置为下列常数之一,利用它可以确定 RDO 和 Client
Batch 游标库如何管理更新操作。
■ rdUpdate(缺省):对每个修改过的数据行使用一个 UPDATE SQL 语句。
■ rdInsertDelete:对每个修改过的数据行使用一对 SQL 语句: DELETE 和
 INSERT。
 即使选择了 rdInsertDelete 选项,Client Batch 游标库还是将操作封装在一个事务
当中,以确保在操作失败时整个操作能够滚回。
设置 UpdateCriteria 属性
在对基本表中的数据行进行更新之前,可能需要知道该行是否被别人修改过,
UpdateCriteria 属性可以决定 Client Batch 游标库如何进行这种检查。在代码正在
编辑该数据行时,如果其它用户已经修改了数据,该属性将采用一种 RDO 技术,确保
不会随意覆盖其它用户作的修改。
通过设置 UpdateCriteria 属性,可以确定 RDO 如何建立 WHERE 子句,用于本地结果
集和远程数据库表之间的同步。BatchUpdate 方法为 Status 属性不为
rdRowUnmodified 的每一个数据行创建一个 SQL 语句。每个语句都包含一个 WHERE 子
句,唯一标识了受到影响的一行数据。通过调整 UpdateCriteria 属性,可以使应用程
序适应于远程服务器的更新机制。例如,在某些服务器中,如果未被修改的数据列被包
含在 SQL 更新内,触发器可能会被不必要地触发。
UpdateCriteria 属性决定如何标识单独的数据行,详细说明如下:
■ rdKey(缺省):在 WHERE 子句中使用关键列。即:RDO 使用主键所在的列,这些
列唯一地标识了表中的特定数据行。
■ rdKeyAndUpdatable。在 WHERE 子句中使用关键列和所有更新过的列。在这种情况
下,RDO 把修改过的列也添加到 WHERE 子句中,以确认这些行已经被修改过。
■ rdKeyAndModified。在 WHERE 子句中使用关键列和所有的列。在这种情况下,RDO
在 WHERE 子句中包含了这个关键列和所有其它列,以确认这些数据行已经被修改过。
■ rdKeyAndTimestamp。如果有 TIMESTAMP 列,使用 TIMESTAMP 列(如果结果集中没
有 TIMESTAMP 列,这将产生一个运行时错误)。该选项将已存在的 TIMESTAMP 列与数
据库中的这一列相比较,以确认数据值是否被修改过。
 提示   如果使用 SQL Server 6.5,在执行批更新时,则可以使用 SQLTrace 工具来
观察 RDO 如何建立 WHERE 子句。

(三) 使用Status属性管理批更新错误
Status 属性用于记录对 Client Batch 游标结果集中单独数据行所作的修改。如果创
建了一个 Client Batch 游标,并且执行了 Edit、Delete 或 AddNew 方法,然后使用
了 Update,那么 RDO 将设置该行的 Status 属性,指明对该数据行进行了什么操作。
最初,从远程服务器获取的 rdoResultset 数据行都被标记为 rdRowUnmodified。但
是,一旦对该结果集进行了修改,RDO 将修改 Status 属性以反映该操作。例如,假如
对 rdoResultset 对象使用了 AddNew 和 Update 方法,Status 属性将被设为
rdRowNew。如果希望 RDO 和 Client Batch 游标库忽略所作的改动,可以在使用
BatchUpdate 方法之前将 Status 属性设置为 rdRowUnmodified。
在 BatchUpdate 方法返回后,每一个数据行的 Status 属性表明了每个更新操作的成
功或失败程度。代码必须检查每一行的 Status 属性,根据状态码进行适当的操作。以
下是可能返回的状态码:
■ rdRowUnmodified。该数据行没有被修改过,或者已经更新成功。不需任何动作。
■ rdRowModified。该数据行已经被修改,但在数据库中没有更新。这种状态可能表明
该行需要更新,或者是因为还没有执行 BatchUpdate 方法,或者因为更新操作失败。
■ rdRowNew。该数据行是由 AddNew 方法插入的,但在数据库中没有更新。这种状态
可能表明该行需要被添加到数据库中,或者是因为还没有执行 BatchUpdate 方法,或
者因为添加操作失败。
■ rdRowDeleted。该数据行已经用 Delete 方法删除了,但数据库中的内容没有更
新。这种状态可能表明该行需要从数据库中删除,或者是因为还没有执行 BatchUpdate
方法,或者因为删除操作失败。
■ rdRowDBDeleted。该数据行已经从记录集和数据库中删除。不需任何动作。

十二. 使用 ODBC API 函数介绍 RDO 对象支持的 ODBC 句柄。
rdoEnvironment, rdoConnection, rdoQuery 和 rdoResultset 对象都支持 ODBC 句
柄,可以使用 ODBC 句柄和 ODBC API 来操纵这些对象。ODBC API 的 hEnv, hDbc 和
hStmt 属性分别对应于 rdoEnvironment, rdoConnection 和 rdoResultset 对象。
下表列出了 RDO 创建的 ODBC 句柄。可以在自己的 ODBC API 代码中使用这些句柄。
RDO 对象属性    ODBC API 创建的句柄

rdoEnvironment.hEnv   SQLAllocEnv
rdoConnection.hDbc   SQLAllocConnect、SQLDriverConnect
rdoResultset.hStmt   SQLAllocStmt
rdoQuery.hStmt    SQLAllocStmt
rdoPreparedStatement.hStmt SQLAllocStmt
注意   虽然允许用 ODBC API 来操作 RDO ODBC 句柄,但还是要小心,如果不正确地
使用 ODBC API,可能会导致不可预知的错误。例如,假如使用 ODBC API 代码来关闭
连接或释放这些 ODBC 句柄中的任何一个,那么 RemoteData 控件或 RDO 的行为将是
不可预知的。保存 ODBC 句柄以供备用也是没有意义的,因为它们是可变的。
详细信息   关于与远程数据库相关的 ODBC API 的深入指导,请参阅“使用 ODBC
 API”。

十三. 使用 RemoteData 控件使用 RemoteData 控件,如何通过被绑定的控件检索数
据。
RemoteData 控件在大多数的方面类似于 Visual Basic Data 控件。二者都用于将数据
识别的被绑定控件连接到数据源。它们的主要区别是: RemoteData 控件使用 RDO 连
接到 ODBC 驱动程序管理器,而 Data 控件则使用 DAO 连接到 Jet 数据库引擎,后者
可以连接到 Jet 数据库,也可以连接到 IISAM 数据源。Data 控件也可以通过
ODBCDirect 接口间接与 RDO 连接。尽管这两种控件都使用相同的 ODBC 驱动程序管理
器和数据源项,但是,RDO、ODBCDirect 和 DAO 不能共享数据源连接、数据对象以及
其它一些资源。
注意   Data 控件支持通过 ODBCDirect 选项使用 RDO DLL。在这种情况下,代码要使
用 DAO,而不是 RDO,来管理 Data 控件及其结果集。
 如果没有 RemoteData 控件,也没有 Data 控件或其它等价的控件,数据识别(被绑
定的)的控件将不能自动访问数据。可以使用 RemoteData 控件执行许多简单的远程数
据访问操作,不需编写任何代码。然而,绝大多数的应用程序都依靠额外的 RDO 代码
来执行大部分的数据访问操作,因为 RDO 提供了更大的灵活性和其它选项,这是
RemoteData 控件不能单独提供的。在很多方面,RemoteData 控件非常类似于 Data 控
件。
被绑定到 RemoteData 控件的数据识别控件会自动显示当前行的一列或多列,有时也会
显示与当前行相邻的若干行。RemoteData 控件对当前行执行所有操作。
如果命令 RemoteData 控件移动到另一个数据行,那么所有被绑定的控件都会将修改传
递到 RemoteData 控件,以便保存到 ODBC 数据源中。然后 RemoteData 控件移动到指
定行,将数据从当前行传到被绑定控件,并在其中显示。
使用 RemoteData 控件处理错误
RemoteData 控件能够自动处理各种可能发生的情况,包括空结果集、添加新的数据
行、编辑并更新已有的数据行等等,以及某些类型的错误处理。但是,在一些较为复杂
的应用程序中,可能会遇到 RemoteData 控件所不能处理的一些错误情况。例如,如果
远程服务器在访问数据源时出现了问题,没有权限,或者不能按照代码执行查询,那么
将导致一个可以捕获的错误。如果错误发生在应用程序开始之前,或者由于某些内部错
误,将会触发 RemoteData 控件的 Error 事件。
RemoteData 控件使用并创建本章讨论的 RDO 对象。在适当设置 RemoteData 控件属性
之后,RemoteData 控件可以创建一个 rdoResultset,与其它 rdoResultset 一样,可
以在代码中控制它。也可以在代码中创建一个 rdoResultset,并将 RemoteData 控件
的 Resultset 属性指向该对象。
注意   为了使用 RemoteData 控件,必须将其添加到 Visual Basic 工具箱中。要将
RemoteData 控件添加到工具箱,请单击“工程”菜单,然后单击“部件”;在“部
件”对话框中,单击“控件”标签,并选择“Microsoft RemoteData 控件 2.0”复选
框。
只有 32 位的操作系统(Windows 95 或 Windows NT)才能支持 RDO。要使用远程数据
对象,必须在Visual Basic “引用”对话框中设置对 Microsoft Remote Data Object
2.0 对象库的引用。

(一) 理解 Remote Data 控件操作
一旦应用程序开始运行,如果在设计时设置了足够的属性,Visual Basic 将使用
RemoteData 控件建立与 ODBC 数据源的连接。这将使用控件的 DataSourceName、
UserName、Password、Options、CursorDriver、LoginTimeout、Prompt 和 Connect
属性创建一个rdoConnection 对象。注意,所有这些属性对于 RemoteData 控件都是唯
一的,但是 Data 控件通过其它属性将它们全部表现出来,如下所示:

RemoteData 控件属性  Data 控件属性

Connect     Connect.
CursorDriver    (仅对 ODBCDirect)
DataSourceName   DatabaseName.
LoginTimeout    嵌入 Connect 属性的连接字符串中。
Options      Options.
Password     嵌入 Connect 属性的连接字符串中。
Prompt     (仅对 ODBCDirect)
UserName    嵌入 Connect 属性的连接字符串中。
如果 RemoteData 控件属性所提供的信息不够,ODBC 驱动程序管理器将显示一个对话
框,以获取漏掉的参数,除非设置了 Prompt 属性使该对话框无效。如果用户或代码没
有提供足够的参数,那么将导致一个可以捕获的错误。
如果建立了连接,RemoteData 控件将根据用来建立连接的值,设置或重设
Environment、Connection、DataSourceName、Transactions 及 Connect 属性。
一旦连接被建立,RemoteData 控件立即使用 SQL、CursorDriver、Options、
LockType、ErrorThreshold 和 ResultsetType 属性执行一个对应于该数据源的查询。
这样就创建了一个 rdoResultset 对象,并设置了 Resultset、ResultsetType 和
Updatable 等属性。在缺省的情况下,将创建一个只读的静态 rdoResultset。在创建
rdoResultset 时 StillExecuting 属性被设为 True。如果希望取消该查询并设置了
rdAsyncEnable 选项,那么,可以使用 rdoResultset 的 Cancel 方法中止该查询。
一旦 rdoResultset 得到第一行数据,StillExecuting 属性将设为 False,
RemoteData 控件将列数据传送到每一个需要数据的被绑定控件。只要从该查询返回了
数据行,rdoResultset 对象的 EOF 属性就会返回 False,并将 RowCount 属性设为一
个非零值。如果 rdoResultset 没有返回数据,RemoteData 控件的行为将由
EOFAction 属性决定。例如,如果 EOFAction 设为 rdAddNew,RemoteData 控件的
Validation 事件将对当前行触发,然后自动执行 AddNew,随后将对新的数据行触发一
个 Reposition 事件。
注意   确认每一个被绑定的控件的 DataField 属性都必须与 rdoResultset 返回的
rdoColumn 对象的 Name 属性相对应,或者是一个空串 ("")。否则将发生一个可以捕
获的错误。
定位当前行指针
用户可以通过鼠标来操纵 RemoteData 控件。他们可以通过单击控件逐行移动当前行指
针,或者移动到 rdoResultset 的开头和结尾位置。RemoteData 控件不允许用户使用
鼠标移出 rdoResultset 的两端。不能设置焦点到 RemoteData 控件。
编写有关重定位当前行指针的代码时,请记住以下几点:
■ 在每次重定位之前,需要查询被绑定的控件的数据是否发生了改变。Validate 事件
被触发,如果没有被 action 参数取消,数据源将根据新的数据进行更新。
■ 在 RemoteData 控件定位到数据源的一个新行之后,列数据会被传送到被绑定的控
件,并触发 Reposition 事件。
■ 一旦到达了 rdoResultset 的两端,RemoteData 控件的操作将由 EOFAction 和
BOFAction 属性决定。
■ 可以对 rdoResultset 使用 MoreResults 方法结束对当前结果集的处理,并确定是
否由另外的可用结果集。如果 MoreResults 返回 True,处理 rdoResultset 的过程将
重新开始,就象执行了一个新的查询一样。前一个结果集将不再有效。

(二) 用程序创建一个rdoResultset对象
有时候需要创建一个独立于 RemoteData 控件的 rdoResultset,但它仍然需要使用被
绑定的控件来访问数据列。这时只要创建一个 rdoResultset,并将其指定到
RemoteData 控件的 Resultset 属性。
注意 Resultset 属性只有在运行时才能使用。
 在代码中设置 RemoteData 控件的属性并使用 Refresh 方法,即可用程序创建
RemoteData 控件的一个 rdoResultset 对象。已经存在的 RemoteData 控件的
rdoResultset 将被放弃。
RemoteData 控件创建的 RDO 对象可以独立于控件被操作,而且无论是否使用被绑定的
控件都可以。rdoConnection 和 rdoResultset 对象都有各自的属性和方法,可以在过
程中使用它们。另外,RemoteData 控件提供了 Environment, Connection,
RowsetSize 以及其它一些属性,可以用于创建 rdoResultset,这是通过 Resultset
属性显示的。
例如,rdoResultset 对象的 MoveNext 方法将 rdoResultset 的当前行往后移动了一
行。要用 RemoteData 控件所创建的 rdoResultset 调用该方法,可以使用以下代码:
MSRDC1.Resultset.MoveNext

如果需要使用 RemoteData 控件执行参数查询,可以在 rdoQuery 的基础上创建一个
rdoResultset,然后将 RemoteData 控件的 Resultset 属性设置为新创建的
rdoResultset。使用这种方法也可以访问另一个 rdoEnvironment。

(三) Remote Data 控件异步操作
在 RemoteData 控件创建 rdoResultset 之前,如果将 RemoteData 控件的 Options
属性设置为 rdAsyncEnable,在 rdoResultset 包含数据行之前控制权将返回到应用程
序。与其它的异步 RDO 编程类似,需要检查 rdoResultset 对象的 StillExecuting
属性,确定什么时候第一行数据成为可用的。要取消该查询,可以使用
rdoResultset.Cancel 方法。在查询完成后,将触发 RemoteData 控件的
QueryCompleted 事件,表明可以使用 rdoResultset 的数据了。
如果没有用 rdAsyncEnable 指定异步操作,那么,在取得 rdoResultset 的第一行数
据之前,将不能执行其它的 Visual Basic 操作,也不能处理事件。但是,在创建结果
集时,其它的 Windows 应用程序可以继续执行。
在使用 RemoteData 控件创建 rdoResultset 对象时,远程数据库引擎自动将
rdoResultset 对象的填充作为后台任务执行。因此,所有已有的书签都会被保存,用
户不需要处理 RemoteData 控件,也不需要在代码中使用 MoveLast 方法。另外,用于
创建 the rdoResultset 的页面锁定能够更迅速地释放,从而使其它的 rdoResultset
对象可以访问相同的数据。
也可以使用 OpenResultset 方法创建一个 rdoResultset 对象,并将 Resultset 属性
设置为新的 rdoResultset。此后,将重设 RemoteData 控件的相应属性,以反映新的
rdoResultset 和 rdoConnection。

(四) 截取Remote Data控件错误
RemoteData 控件要处理开发过程中各个不同阶段发生的错误。在其中的每个阶段,都
需要以代码或其它形式进行不同类型的错误管理:
开发阶段        RemoteData 控件如何处理错误

例如,在设置 RemoteData 控件属性, Visual Basic 显示错误对话框,说明存在的问
题。
以访问指定数据库时。
在包含 RemoteData 控件的窗体被  RemoteData 控件触发 Error 事件。
加载之前(Form_Load 完成之前)。
在包含 RemoteData 控件的窗体  RemoteData 控件或 RDO 触发一个可以捕获的错误。
被加载之后(Form_Load 完成之后)。
用户单击 RemoteData 控件按钮时。 RemoteData 控件触发一个 Error 事件。
在 Form_Load 事件之后,   RemoteData 控件触发一个 Error 事件。
RemoteData 控件试图打开一个
rdoConnection 并创建
rdoResultset 对象。
自定义控件执行了一个操作,例如  RemoteData 控件触发一个 Error 事件。
MoveNext 方法、AddNew
方法和 Delete 方法。

在 Error 事件触发后,可以亲自处理错误,也可以让 Visual Basic 显示一个对话
框,将错误报告用户。

第四节 使用 ODBC API
 使用 ODBC API 详细介绍了如何在 Visaul Basic 代码中直接使用 ODBC API 访问远
程数据库服务器,并且介绍了连接管理、错误处理、游标选项和结果集管理的实现原
则。
 (不使用,不介绍)

第五节 使用VBSQL API
 使用 VBSQL API 概述了针对 Microsoft SQL 服务器编程模型的 Visual Basic 库
(VBSQL) 的特性和功能。VBSQL 可以直接访问绝大多数 SQL 函数。
 (不使用,不介绍)



--
☆ 来源:.BBS 荔园晨风站 bbs.szu.edu.cn.[FROM: AnonymousOrganizatio]
--
※ 转载:·BBS 荔园晨风站 bbs.szu.edu.cn·[FROM: 192.168.1.90]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店