第十九章:设计客户/服务器应用程序

Visual FoxPro 为创建功能强大的客户/服务器应用程序提供了一些专用工具。Visual FoxPro 客户/服务器应用程序将 Visual FoxPro 功能强、速度快、图形化的用户界面以及高级的查询、报表和处理等优点与严密的多用户访问、海量数据存贮、内置安全性、可靠的事务处理和日志以及 ODBC 数据源或服务器的本地语法等功能紧密地结合在一起。Visual FoxPro 和强有力的服务器之间的协作可以为用户提供功能强大的客户/服务器解决方案。

要创建一个成功的客户/服务器应用程序最重要的是一个好的设计方案。本书前面的章节提供了多用户应用程序开发的有关信息。本章以此内容为基础,这里讨论的是开发客户/服务器应用程序的方法。

如果想要了解建立和升迁一个本地客户/服务器原型程序的有关内容,请参阅第二十章“升迁 Visual FoxPro 数据库”。有关使用 SQL pass-through 技术的详细内容,请参阅第二十一章“实现客户/服务器应用程序”。若要加快数据检索和处理的速度,请参阅第二十二章“优化客户/服务器性能”

本章内容要点:

客户/服务器应用程序的设计目标

在设计一个客户/服务器应用程序时,必须首先了解各方面的需求,权衡利弊。您可能既想为自己的用户建立一个速度很快、效率极高的应用程序,又想保证应用程序数据的完整性,尽可能地利用现有的硬件设备,并考虑程序将来的可扩展性。同时,做为一个 Visual FoxPro 开发人员,您还想使开发过程尽可能得简单,并降低开发费用。

达到上述要求的最佳方法是在设计应用程序时把这些目标牢记在心。下面我们就给出一些基本的开发技巧和相关技术,帮助您设计高性能的客户/服务器应用程序。

高性能的设计

用 Visual FoxPro 创建一个快速、高性能的客户/服务器应用程序涉及到如何利用 Visual FoxPro 引擎的惊人速度的问题。可以考虑使用一些新技术来实现这一目的,例如,使用基于集合的数据访问技术而不是传统的本地定位技术;创建参数化查询仅下载符合需求的数据;将表安排在优化的平台上;平衡使用 Visual FoxPro 和远程存储过程等。

在使用这些新技术之前,必须首先对将要使用的系统进行分析。在设计一个本地或文件服务器应用程序时,必须确定应用程序将要使用或创建的查询、表单、菜单和报表。在设计一个客户/服务器应用程序时,除了这些常规的系统分析之外,还需要针对客户/服务器应用程序的特点进行分析,考虑查询、表单、菜单和报表所使用的数据存放在何处,如何访问这些信息等。例如,您必须考虑下列一些问题:

在确定了客户/服务器应用程序的基本组件之后,就可以开始设计应用程序访问和更新数据的方式了。

仅下载所需要的数据

要使客户/服务器应用程序运行速度快并且效率高,应考虑的一个最重要的因素就是尽量减少从服务器上下载的数据。由于客户/服务器应用程序可能要访问远程服务器上潜在的大量数据,使用传统的本地定位技术会导致客户/服务器应用程序运行速度的降低。为了加快执行速度,可以使用基于集合的数据访问技术来过滤将要下载的数据。

有效访问基于集合的数据

远程数据是基于集合的数据:要访问远程数据,必须使用 SQL SELECT 语句从一个大的数据存储地点中选择一个数据集合。建立一个传统的本地应用程序和创建一个客户/服务器应用程序的最重要区别在于传统的 Visual FoxPro 定位技术和基于集合的服务器数据访问技术的区别。

使用传统的定位技术

在传统的本地数据库程序设计中,可以使用 GO BOTTOM 命令查询访问离散的而且往往是大量的数据。通过执行 SET RELATION 命令在两个表之间创建一个临时关系,可以在数据中定位,然后通过 SKIP 命令在相关的记录中移动。

当这种定位记录的方法应用于远程数据时,如果远程数据量巨大,将会导致访问效率低下。例如,假定为了访问远程数据源中的一个大表,首先创建了一个远程视图,然后执行 GO BOTTOM 命令。这时,视图开始从数据源获取所有数据,然后通过网络传送数据,最后加载到本地系统的视图临时表中,这段检索操作需要经历漫长的等待过程。

使用参数化查询

访问远程数据的一种更为有效的方法是每次仅下载所需要的数据,需要取得其他额外的记录或新记录时,再重新执行查询。可以使用参数化的 SELECT 语句下载特定的小数据集合,通过使用 REQUERY( ) 函数可以请求新的数据集以访问新记录。

不能对远程服务器数据发出 GO BOTTOM 命令,因为这样做具有以下缺点:

例如,若要建立一个客户/服务器应用程序来查阅特定客户的订单。可创建一个远程视图来访问 Customer 表,然后创建另一个远程视图来访问 Orders 表,并基于 cust_id 字段将该视图参数化。这样,就可以把当前的顾客记录做为 Orders 表的视图的查询参数。

可以使用参数给下载的数据集规定一个范围,使其下载的数据量比较适中。如果一次请求的数据量过少,会导致对远程服务器的查询工作过于频繁,因而降低了程序的性能。而如果请求过多的数据,则会因为下载不需要的数据而浪费时间。

选择最佳的客户/服务器设计方案

下面将举例介绍获得客户/服务器技术优势的方法,以及如何避免因编程方法不当而造成的隐患。第一种方法使用传统的编程方法检索数据,从一个远程数据源中获取所有的数据,下载到本地临时表中,然后用 SET RELATION 命令将这些临时表联系起来。而第二、三、四种方法逐步应用更有效的获取数据的技术,使用“just-in-time”方法有效地限制下载的数据量,从而提高网络的响应时间并获取最新的数据。

使用一种未经优化的客户/服务器策略

对远程数据使用本地数据的定位技术是一种直接的未经优化的客户/服务器解决方式。例如,假定某一远程数据源中存有一千万条客户记录和一亿条订货记录,可以创建一个不考虑效率的应用程序,把所有客户和订货记录下载到本地临时表中。然后对一亿条订货记录索引,在本地临时表中对 Customer 和 Orders 表建立一个临时关系,使用 SKIP 命令来定位记录。

这种方法未考虑性能的优化,不过,如果“一”方在本地而“多”方在远程,这种方法可能还是可行的。

筛选多方

对上面这种方法稍加改进,可以得到另一种客户/服务器解决方案。这种方案就是减少关系中“多”方数据的下载,但仍然下载“一”方的所有记录,所以可以跳过一些记录。在这种方案下,需要创建一个远程视图来显示关系中的“多”方,也就是 Orders 表,该视图可用客户的 ID 值作为参数。但这时,仍然需要下载整个 Customer 表。

虽然对 Orders 表创建一个参数化视图相对于下载所有的订单信息来说是一个改进,但是,在下载整个 Customer 表的同时,仍然带来了一些不必要的信息。Customer 表也同样存在过时的危险,因为在系统上的其他用户会对其进行修改。这种方案在关系的“一”方仅包含少量数据的时候还是可行的。

筛选一方

更好一些的客户/服务器解决方案是为所有的远程数据创建远程视图。在 Customer 表的远程视图中,使用 SELECT 语句选择某个地区的客户,这样就可以限制下载到远程视图中的客户记录的数量。然后再对关系中“多”方 Orders 表创建一个远程视图,并以客户 ID 值为参数。

这种方案检索更少量的记录。使用 SKIP 命令可以在关系的“一”方(即 Customer 视图)中移动。使用 REQUERY( ) 函数可以访问“多”方(即 Orders)中的新数据。

在这种方案中,既限制(或筛选)了关系中的“一”方又筛选了关系中的“多”方,但仍可以使用 SKIP 命令在筛选过的数据之中定位。如果关系中的“一”方经过筛选以后,仍然能够提供足够的信息满足后续的一些查询,而不需要再次向远程服务器发出请求,那么推荐使用这种方案。

使用主关键字访问一对多关系

效率最高的客户/服务器解决方案是放弃使用开销大的 SKIP 命令,而创建一个表单,让用户输入或选择一个客户 ID,然后把它既做为 Customer 表的远程视图参数,又做为 Orders 表的远程视图参数。

例如,可以创建一个一对多表单,其中“一”方显示客户信息,而表格控件显示关系中的“多”方。该表格可以与表单“一”方中选定的客户 ID 绑定。然后可以把 CURSORSETPROP( ) 的 MaxRow 属性设置为 1,使用下列代码填充表单的“一”方:

SELECT * FROM customer WHERE customer.cust_id = ?cCust_id

当用户想查看另一个客户的记录时,必须输入或选择一个新客户 ID。表单用新客户的 ID 值重新查询订单中的数据资源,然后用新的订单数据刷新表格控件。

使用这种技术,应用程序仅仅下载需要的数据。由于减少了下载的数据量,从而提高了网络的响应速度,而且,在显示所请求的信息之前能够重新查询数据,这样总能提供最新的信息。

想用任意主关键字值随机访问一对多关系时推荐使用这种方法。在打开一个表单时,还可以把一些主关键字下载到控件中,比如一个下拉式列表,并且提供一个控件,让用户在需要的时候进行选择,以达到刷新主关键字值列表的目的。

在客户/服务器应用程序中使用数据环境

在表单或表单集中使用远程数据时,请将视图加入到表单或表单集的数据环境中。可以把数据环境的 AutoOpenTables 属性设置为“假”(.F.),这样便可以指定什么时候应用程序用远程数据对视图进行刷新。在调用数据环境的 OpenTables 方法程序之后,可为文本框或其他数据绑定型控件设置 ControlSource 属性,一般是在与表单的 Init 事件相关联的代码之中进行设置,有关设置表单属性的详细内容,请参阅第九章“创建表单”

在最佳平台上放置数据

把数据库的数据和其他属性保存在最佳平台上,可以获得最佳的性能。某一特定元素的最佳平台取决于访问和更新该元素的方法。例如,可以将邮政编码一类的服务器表保留一个本地副本,用于一个查阅表,只有当后台的表更改时,才对本地副本进行刷新。

下表列出一些常用的应用程序元素,以及在何处放置这些元素可以获得最佳性能。

根据平台放置元素

元素 位置 类型 注释
本地 服务器的查阅表的本地副本;或小型的、不经常更改的表 如果远程服务器支持,可以使用时间戳来比较并且选择性地刷新本地表,以使得对本地表的更改与后台源表的更改同步。
远程 大型的或经常更改的表
规则 本地 在远程视图中的规则 可以使用 DBSETPROP( ) 把字段和记录级规则保存在远程视图中。在应用程序中可以使用这些本地规则来检查数据的有效性,然后再把这些数据送到后台做为对远程表的更新。
远程 远程基表的行级别和列级别的规则
存储过程 本地 Visual FoxPro 存储过程
远程 后台服务器的存储过程 使用 SQL pass-throughh函数 SQLEXEC( ) 可调用服务器的存储过程。
事务 本地 Visual foxPro 事务
远程 服务器事务
触发器 本地视图 在视图上无触发器
远程 服务器触发器

为了减少查找时的网络传输,不仅可以把不常更改的表保存在本地,而且可以把经常更改的查阅表也保存在本地。例如,可以下载公司的客户列表,只有当客户信息更改时才对其进行刷新。

为了做到这一点,可以编写应用程序来比较表的本地副本时间戳和后台数据时间戳(如果远程服务器支持时间戳)。只有当服务器的表更改时,才更新本地副本。也可以在表单中增加一个命令按钮,用于强制实时对表的立刻下载,这样用户一旦需要,便可刷新他们的本地表副本。

选择正确的方法

可以使用远程视图或 SQL pass-through,或者联合使用二者,来创建客户/服务器应用程序。可以把两者结合起来获得强大的功能:使用视图来满足数据管理的大部分需要,使用 SQL pass-through 来增强应用程序的功能。

使用视图

可以使用视图做为开发强大的客户/服务器应用程序的核心方法。远程视图是一个强有力的技术,用它可以从一个远程服务器中选择所需的数据并下载到一个本地 Visual FoxPro 临时表中,同时可以查看并更新远程数据。一个视图基本上是使用 SQL SELECT 语句所得到的一个结果集合。

视图具有长期性,因为视图定义保存在数据库中。视图定义具有可以设置的属性,借助这些属性可对活动视图的临时表做进一步定制。视图是创建可更新的结果集合的最佳数据定义工具。

可以使用本地视图建立一个本地原型,然后使用“升迁向导”把本地视图转换为远程视图。有关使用升迁向导的详细内容,请参阅第二十章“升迁 Visual FoxPro 数据库”

如果应用程序用户想在动态的工作中使用数据,可应用脱机视图。脱机视图便于随身携带数据,允许膝上型或其他便携式计算机用户操作源数据的存储副本,在旅途上他们可以更新副本。当用户与服务器相连时,应用程序可以很方便地将脱机数据的更改合并到源表中。

您可能也想使用脱机视图技术,以便本地用户“脱机”操作数据,过一段时间后再合并它们的更新。有关脱机数据操作的详细内容,请参阅第八章“创建视图”

使用 SQL pass-through

SQL pass-through 技术使用 Visual FoxPro SQL pass-through 函数对远程服务器进行直接访问。这些函数提供了视图所不能提供的服务器访问和控制能力。例如,可以在远程服务器上完成数据定义,设置服务器属性,并且访问服务器存储过程等。

SQL pass-through 是创建只读结果集合和使用其他服务器当地 SQL 语法的最好工具。视图是 SQL SELECT 语句的结果集合,SQL pass-through 与此不同,它允许使用 SQLEXEC( ) 函数向服务器发送符合服务器语法的任何命令。下表列出了 Visual FoxPro 的 SQL pass-through 函数:

SQL pass-through 函数

SQLCANCEL( ) SQLCOLUMNS( ) SQLCOMMIT( )
SQLCONNECT( ) SQLDISCONNECT( ) SQLEXEC( )
SQLGETPROP( ) SQLMORERESULTS( ) SQLPREPARE( )
SQLROLLBACK( ) SQLSETPROP( ) SQLSTRINGCONNECT( )
SQLTABLES( )

使用 SQL pass-through 技术,可以自己创建临时表。尽管 SQL pass-through 提供了更直接的服务器访问方式,但是相对视图来说,这种访问方式缺乏持久性。视图定义永久地保存于数据库中,而 SQL pass-through 与此不同,它所创建的临时表只存在于当前的工作期内。有关使用 SQL pass-through 技术的详细内容,请参阅第二十一章“实现客户/服务器应用程序”

视图和 SQL pass-through 的结合

建立一个 Visual FoxPro 客户/服务器应用程序的最有效办法是结合使用视图和 SQL pass-through 技术。由于视图比较容易创建,而且能够提供自动缓冲和更新功能,因此可以使用视图完成大部分的数据管理任务。同时,还可以使用 SQL pass-through 执行远程服务器上的特定任务,例如数据定义和服务器存储过程的创建和执行等。

快速开发应用程序

无论采用何种编程方法,总是需要一种策略使得开发客户/服务器应用程序快速而有效。由于 Visual FoxPro 可以快速地创建程序原型和建立应用程序,因此可以首先为应用程序建立一个本地原型,然后根据远程数据源再升迁程序,分阶段完成程序。如果想在开发过程中就能访问远程数据源,那么可以考虑使用远程视图来建立应用程序原形,以便访问远程数据。

使用视图生成原型

开发 Visual FoxPro 客户/服务器应用程序的第一步就是建立一个原型。通过以模块方式建立应用程序原型,可以在开发过程中尽早发现应用程序的哪些方面应该修改和增强。使用这种方法,可以只用小批量的数据精心调整程序,从而避免了处理远程异构大数据集时与其复杂规则打交道的麻烦。建立原型的方法将在第二十章“升迁 Visual FoxPro 数据库”中介绍。

使用本地视图创建一个本地原型

客户/服务器应用程序的本地原型也就是一个有效的使用本地视图访问本地表的 Visual FoxPro 应用程序。由于最终的客户/服务器应用程序将使用远程视图访问远程数据,因此可在应用程序原型中也使用视图。通过使用本地视图建立应用程序的原型,又距离最终的应用程序近了一步。

如果在开发阶段不必总访问远程数据,或不想使用远程数据来建立应用程序原型,那么生成一个本地原型是更实际的办法。本地视图访问本地 Visual FoxPro 表,而不是远程数据源表。不过,可以模拟服务器上的数据结构来创建本地数据。使用本地数据代替远程数据是一种快速开发和测试应用程序基本设计方案的理想方法。可以限制选入视图的数据量来加快开发过程。有关生成本地和远程视图的详细内容,请参阅第八章“创建视图”

准备升迁

所谓升迁就是使用与原来的 Visual FoxPro 数据库相同的表结构、数据和内在的许多其他属性,在远程服务器上创建一个新的数据库。利用升迁可以把现有的 Visual FoxPro 应用程序转变为一个客户/服务器应用程序。有关升迁的详细内容,请参阅第二十章“升迁 Visual FoxPro 数据库”

如果设计的应用程序准备在最后升迁,那么在设计应用程序的结构和编写模块时就需要仔细筹划,为如何发挥远程数据源的最佳性能做考虑。有关这方面的内容在本章前面的“高性能的设计”一节中做了描述。

使用远程视图建立程序原型

在开发客户/服务器应用程序的时候,如果能够访问一个远程数据源并且希望直接使用远程数据,可以使用远程视图建立程序原型。当使用远程视图来生成原型时,便可跳过升迁这个步骤,因为数据已经存放在远程服务器上,并且已经可以使用远程视图来访问这些数据。

实现客户/服务器应用程序

通过分阶段地完成应用程序原型,可以简化对应用程序的测试和调试工作。在完善一个应用程序原型过程中,可以分阶段、有系统地加入多用户功能,并把数据转移到远程数据源,然后逐个模块地测试和调试应用程序。

在实现应用程序时,可以借助 SQL pass-through 技术,使用服务器本地语法,访问服务器的特有功能,比如,服务器存储过程等。有关 SQL pass-through 的详细内容,请参阅第二十一章“实现客户/服务器应用程序”

优化应用程序

如果程序对远程数据的操作功能已经完成,并且已经通过了测试和调试阶段,那么就可以开始考虑如何对整个应用程序的速度和性能进行优化。有关优化应用程序性能的详细内容,请参阅第二十二章“优化客户/服务器性能”

确保开发的准确性和数据的完整性

可以把 Visual FoxPro 的数据有效性规则和存储过程,以及数据源的数据有效性规则和存储过程结合起来,使生成的客户/服务器应用程序能够有效地维护数据完整性。

维护数据完整性

可以创建远程服务器有效性规则的本地版本,为用户提供一些信息。例如,有可能因为输入的数据破坏了某些服务器关系的完整性,或者破坏了数据有效性规则,导致不允许将某些更新数据发送给后台。

在远程视图和脱机视图中使用 Visual FoxPro 规则

可以在远程视图和脱机视图中创建字段级和记录级规则,在数据发送给远程数据源之前,在本地检查输入数据的有效性。由于这些视图规则的目的在于防止发送的数据违反服务器的数据完整性规则,由此更希望将数据源的规则直接复制到远程视图的规则中。使用 DBSETPROP( ) 函数可以为视图创建规则。

提示 可以在远程视图中创建一个本地有效性规则,该规则调用远程服务器存储过程,并且把需要检查其有效性的值做为参数发送给服务器。但是,使用远程存储过程会增加数据输入时的处理时间。

使用服务器规则

可以选择服务器上的规则来检查数据的有效性。出现错误时,出错处理程序可以调用 AERROR( ) 函数来获取信息,包括错误信息号、远程出错信息的文本以及该相有关此错误的连接句柄等。

使用服务器触发器

虽然可以在本地表中创建 Visual FoxPro 触发器,但在视图中却不能。不过可以在远程数据源中使用触发器。服务器触发器可以用来处理二级数据更新,比如,级联更新或删除。使用服务器触发器处理二级更新比从 Visual FoxPro 应用程序向远程服务器发送多个命令更有效。

防止数据丢失

Visual FoxPro 和大部分的远程数据源都提供了事务处理日志功能,用来防止数据丢失。有关使用 Visual FoxPro 事务处理的详细内容,请参阅第十七章“共享访问程序设计”

可以利用 Visual FoxPro 事务用于本地原型和本地数据处理。而服务器事务可用于远程数据的更新、插入和删除。有关使用远程事务的详细内容,请参阅第二十二章“优化客户/服务器性能”