打印本文 打印本文 关闭窗口 关闭窗口
多层数据库开发三:创建多层应用程序
作者:武汉SEO闵涛  文章来源:敏韬网  点击数3499  更新时间:2009/4/23 18:30:19  文章录入:mintao  责任编辑:mintao
ection就假设应用服务器与客户程序在同一个计算机上,或者应用服务器在系统注册表中可以找到。反过来说,如果应用服务器没有在客户端注册,而且与客户程序不在同一个计算机上,就须设置ComputerName属性。
  即使应用服务器在客户端注册了,仍然可以设置ComputerName属性重新指定一个另一台计算机上的应用服务器。
  ComputerName属性一般设为计算机的主机名或IP地址,如果指定的主机名或IP地址是非法的或者没有找到,TDCOMConnection就会触发异常。
  如果客户程序需要在运行期间动态地选择应用服务器,最好不要用ComputerName属性来切换,而要加入一个TSimpleObjectBroker构件,再由ObjectBroker属性指定这个TSimpleObjectBroker构件。
3.7.2 用TCP/IP连接
  要使用TCP/IP 方式来连接应用服务器,就需用到TSocketConnection构件。
  TCP/IP 方式是一种应用广泛的连接方式,因为支持TCP/IP环境是相当普遍的。
  TSocketConnection用Address属性或Host属性来定位应用服务器所在的计算机,前者用于指定IP地址,后者用于指定主机名,这两个属性是互斥的,只需要设置其中一个。此外,还要设置Port属性指定端口号,必须与应用服务器上运行的Scktsrver.exe或Scktsrvc.exe所使用的端口号一致,默认值是211。
3.7.3 用OLEnterprise连接
  要使用OLEnterprise方式连接应用服务器,就要用到TOLEnterpriseConnection构件。
  如果要直接连接应用服务器,不通过Business Object Broker,就要设置ComputerName属性指定应用服务器所在的主机名,就好像DCOM方式一样。
  如果要通过Business Object Broker连接应用服务器,就要设置BrokerName属性指定Business Object Broker的名称。
  ComputerName属性和BrokerName属性是互斥的,设置了其中一个,另一个就为清空。
3.7.4 用CORBA连接
  要使用CORBA方式连接应用服务器,就须用到TCorbaConnection构件。
  对于CORBA方式来说,只需要设置RepositoryID属性标识CORBA数据模块的接口,因为局域网中的智能代理(Smart Agent)会自动定位一个可用的应用服务器。
  不过,如果不想让Smart Agent自动定位一个应用服务器,而是想指定一个特定的应用服务器,就要设置HostName属性指定应用服务器的主机名或IP地址。
  TCorbaConnection构件可以通过两种方式获得应用服务器上CORBA数据模块的接口:
  如果要使用静态联编方式,必须把_TLB.pas文件(由类型库编辑器生成)加到客户程序中。静态联编方式不仅能够在编译期进行类型检查,而且运行速度较快。
  如果要使用动态联编方式,CORBA数据模块的接口必须用InterfaceRepository注册。
3.7.5 标识服务器
  前面讲的是怎样定位应用服务器所在的计算机,现在要讲定位了计算机后,怎样标识应用服务器本身。
  如果使用DCOM、TCP/IP或OLEnterprise方式来连接应用服务器,您可以通过ServerName属性或ServerGUID属性来标识应用服务器。其中,ServerName用于指定应用服务器,实际上就是一个已注册的OLE自动化对象名。ServerGUID属性用于指定远程数据模块的全局识别号。如果合法设置了ServerName属性,ServerGUID属性会自动被设置。
  如果使用CORBA方式来连接应用服务器,就要用RepositoryID属性来标识应用服务器上CORBA数据模块的接口。
3.7.6 TSimpleObjectBroker
  如果客户程序需要在运行期间动态地选择应用服务器,最好用TSimpleObjectBroker构件来切换,而不使用ComputerName属性。TSimpleObjectBroker能够自动维护一个可用的应用服务器的列表。当MIDAS连接构件需要连接一个应用服务器时,它就向TSimpleObjectBroker提出申请,TSimpleObjectBroker一般会随机提供一个应用服务器。如果TSimpleObjectBroker提供的应用服务器没法工作,TSimpleObjectBroker会再换一个,一直到MIDAS连接构件与应用服务器建立了连接为止。
  一旦MIDAS连接构件与应用服务器建立了连接,它会自动把应用服务器的有关情况保存到有关属性中,如ComputerName、Address或Host等,因为MIDAS连接构件有可能会断开连接,以后又要再次连接,这时候就不必再向TSimpleObjectBroker提出申请。
  在使用OLEnterprise或CORBA方式的情况下,不要用TSimpleObjectBroker构件,因为这两种方式都有自己专门的中介服务。
3.7.7 开始连接
  进行了上述有关设置后,现在就可以连接应用服务器了。不过,在连接应用服务器之前,最好还要设置TClientDataSet的RemoteServer属性指定一个MIDAS连接构件,再设置ProviderName属性指定应用服务器中的TDataSetResolver 或TProvider构件。
  当客户程序试图访问IProvider接口时,就会自动建立与应用服务器的连接,例如,把TClientDataSet的Active属性设为True。
  当然,也可以通过MIDAS连接构件的Connected属性来连接或断开连接。
  在将要与应用服务器建立连接之前,会触发BeforeConnect事件。当建立了与应用服务器的连接后,会触发AfterConnect事件。
3.7.8 断开连接
  当进行下列操作时会使连接断开:
.把Connected属性设为False。
.关闭客户程序或MIDAS连接构件被删除。
.MIDAS连接构件的ServerName、ServerGUID、ComputerName、Host、Address等属性修改后,也会使原有的连接断开,再与基于新的应用服务器重新建立连接。
  注意:尽量不要使用修改ComputerName、Host等属性的方式来切换应用服务器,最好用TSimpleObjectBroker,或者使用几个MIDAS连接构件分别建立连接。
  与应用服务器的连接将要断开之前会触发BeforeDisconnect事件,连接真正断开之后,会触发AfterDisconnect事件。
3.8 调用服务器上的接口
  通过TClientDataSet的Provider属性可以获得IProvider接口。其实,大部分情况下并不需要直接调用IProvider接口,因为对IProvider接口的调用已经封装在TClientDataSet的属性和方法中,唯一的例外是,DataRequest函数只能通过IProvider接口调用。
  通过MIDAS连接构件的AppServer属性可以获得应用服务器上远程数据模块的接口,通过此接口可以调用远程数据模块的方法,例如:
  MyConnection.AppServer.SpecialMethod(x,y);
  这种调用方式是动态联编的,也就是说,编译器并不检查SpecialMethod的参数,甚至连有没有SpecialMethod它都不管。由于编译器不进行类型检查,在运行期实际调用时有可能失败。而且,动态联编没有静态联编的执行速度快。
  如果用DCOM或CORBA作为通讯协议连接应用服务器,最好采用静态联编方式来访问远程数据模块的接口,方法就是用特定的接口类型对AppServer属性进行强制类型转换。假设远程数据模块的接口叫IMyAppServer(它的上级是IDataBroker),程序示例如下:
With MyConnection.AppServer as IMyAppServer Do SpecialMethod(x,y);
  上面这行代码适合于DCOM方式,下面这行代码适合于CORBA方式:
With IUnknown(MyConnection.AppServer) as IMyAppServer Do SpecialMethod(x,y);
  对于DCOM方式来说,要使用静态联编方式调用远程数据模块的接口,它的类型库必须在客户端注册。要注册类型库,可以调用匼DELPHI4\BIN目录中的TREGSVR.EXE。
  对于CORBA方式来说,要使用静态联编方式调用远程数据模块的接口,必须在客户程序中引用类型库编辑器生成的_TLB.pas文件。
  对于TCP/IP或OLEnterprise方式来说,没法使用真正的静态联编方式来调用远程数据模块的接口,不过,可以通过远程数据模块的调度接口来改善性能,程序示例如下:
  varTempInterface: IMyAppServerDisp;
Begin
TempInterface := MyConnection.AppServer;
...
TempInterface.SpecialMethod(x,y);
...
End;
  要通过调用接口访问远程数据模块,必须在客户程序中引用类型库编辑器生成的_TLB.pas文件。
3.9 在客户端纠错
  SQL Explorer可以把服务器端的纠错和默认表达式引入到一个数据字典中,这样,通过应用服务器上的基于BDE的数据集,客户端就可以借用服务器端的规则对数据纠错,这就是所谓的客户端纠错。如果用户在客户端输入或修改的数据违反了规则,这些数据就不会传递到应用服务器端,更不会传递到RDBMS端。
  由此可见,在客户端纠错可以避免把错误的数据传递给远程数据库服务器,从而节省了网络上不必要的传输,因为这些数据即使传递给远程数据库服务器,也会被退回。
  不过,有时候也需要暂时禁止在客户端纠错。例如,假设有一项纠错规则需要基于字段的最大值,而客户端一次只能检索若干条记录,也就是说,客户端统计出来的最大值与服务器端统计出来的最大值有可能不一样。如果客户端使用了过滤,也有可能使客户端统计出来的最大值与服务器端统计出来的最大值不一样。上述情况下,就需要暂时禁止纠错。
  要暂时禁止纠错,可以调用TClientDataSet的DisableConstraints,DisableConstraints实际上是使一个内部的引用计数加1,当这个引用计数大于0,就不允许纠错。
  要重新允许纠错,可以调用TClientDataSet的EnableConstraints,EnableConstraints实际上是使一个内部的计数减1,当这个计数减到0时,就可以重新允许纠错。
3.10 更 新 数 据
  当客户程序从应用服务器检索到数据,就在内存中建立这些数据的副本。用户对数据进行编辑修改后,TClientDataSet专门用一个Delta属性存储变化了记录,包括更新、删除和插入的记录。为了使修改了的数据永久化,就要向数据库申请更新数据。
3.10.1 更新数据的一般步骤
  首先,客户程序要调用ApplyUpdates函数向应用服务器提出申请,ApplyUpdates函数将通过IProvider接口把Delta属性传递给应用服务器。
  应用服务器收到客户程序的申请后,再向远程数据库服务器提出申请,并且把被远程数据库服务器认为出错的记录暂时缓存起来。应用服务器上的TDataSetProvider或TProvider构件把出错的记录返回给客户程序,其中包括错误信息和错误代码。
  客户程序收到这些出错的记录后,可以进行核对和修改,然后继续更新。注意:如果应用服务器端使用MTS类型的远程数据模块,就无法提供IProvider接口,这种情况下,必须通过远程数据模块的接口直接申请更新数据。
3.10.2 ApplyUpdates函数
  当用户修改了数据后,应当调用ApplyUpdates函数向应用服务器申请更新数据。
  ApplyUpdates函数只有一个MaxErrors参数,用于指定一个最大错误数,如果出错的记录数超过了这个参数的值,此次更新就停止。如果MaxErrors参数设为0,只要应用服务器发现有一个错误的记录,更新操作就停止。如果MaxErrors参数设为-1,当应用服务器发现有错误的记录,就尝试更新下一个记录,等所有的记录都尝试过以后才返回。
  ApplyUpdates函数将返回实际遇到的错误数,同时,应用服务器将返回那些有错误的记录。
3.10.3 核对出错的记录
  当应用服务器把出错的记录返回给客户程序时,将在客户端触发OnReconcileError事件,这样,您就有机会对记录进行核对并修改。OnReconcileError事件是这样声明的:
TReconcileErrorEvent = Procedure (DataSet: TClientDataSet; E: EReconcileError; UpdateKind:TUpdateKind; var Action: TReconcileAction) of object;
  其中,DataSet参数是TClientDataSet构件名,籍此可访问数据集中某字段的NewValue、OldValue、CurValue等属性,从而分析有关出错的原因。通过DataSet参数也能够调用TClientDataSet的方法,但不能进行可能导致数据被修改的操作,否则,可能会引起死循环。
  E参数是一个指向EReconcileError对象的指针,从中可以提取出有关错误信息和导致错误的原因。
  UpdateKind参数表示是哪种操作导致了错误,可以是以下值:ukModify(修改)、ukInsert(插入)、ukDelete(删除)。
  Action参数用于决定在退出处理OnReconcileError事件的句柄后是否继续更新数据集,可以设为下列值:raSkip、raAbort、raMerge、raCorrect、raCancel、raRefresh。下面这个程序示例演示了怎样调用RecError单元中的一个对话框:
Procedure TForm1.ClientDataSetReconcileError(DataSet:TClientDataSet;E:EReconcileError; UpdateKind:TUpdateKind, var Action TReconcileAction);
  Begin
  Action := HandleReconcileError(DataSet, UpdateKind, E);
  End;
3.10.4 刷新记录
  客户程序把数据在内存中建立一个副本,并工作于这个副本,而其他用户有可能已经修改了数据,也就是说,内存中的数据已不是数据库中的实际数据。
  为了使内存中的数据是当前最新的,可以调用TClientDataSet的Refresh。不过,调用Refresh前要保证客户端没有未确定的修改,换句话说,就是客户端的日志中没有记载任何修改,否则会触发异常。
  不过,TClientDataSet的RefreshRecord可以不管当前有没有未决的修改,它可以刷新当前记录,而日志中记载的修改继续保留。
  注意:调用RefreshRecord有可能带来冲突。因此,调用RefreshRecord之前,最好还是检查一下当前是否有未决的修改,如果有的话,就触发异常,程序示例如下:
If ClientDataSet1.UpdateStatus <> usUnModified then
Raise Exception.Create(''''You must apply updates before refreshing the current record.'''');
ClientDataSet1.RefreshRecord;
3.10.5 从应用服务器获取参数
  下列两种情况下,客户程序需要从应用服务器获得参数:
.客户程序需要知道存储过程的输出参数。
.客户程序需要初始化TQuery或TStoredProc的输入参数。
  要从应用服务器获得参数,调用TClientDataSet的FetchParams函数,这个函数将返回有关参数并作为Params属性的值。在设计期也可以获取参数,办法是:用鼠标右键单击TClientDataSet构件,在弹出的菜单中选择“Fetch Params”命令。
  注意:无论是在运行期调用FetchParams函数,还是在设计期使用“FetchParams”命令,客户端必须已经与应用服务器连接并且获得了IProvider接口,因为这些参数是由应用服务器上的TDataSetProvider或TProvider构件提供的,它们的DataSet属性必须指定了TQuery构件或TStoredProc构件。
  也可以在客户端设置Params属性,然后把参数传递给应用服务器。
3.11 自定义应用服务器
  MIDAS的结构是非常灵活的,可以自定义应用服务器以适应实际的需要。您可以从两个方面来自定义应用服务器,一是扩展远程数据模块的接口,二是使用自定义的数据集。
  远程数据模块是应用服务器的核心部件,它提供了应用服务器与客户程序通讯的基本接口,除非客户程序直接调用IProvider接口。
  如果应用服务器使用MTS类型的远程数据模块,客户程序只能通过远程数据模块的接口与应用服务器进行通讯。如果试图绕过MTS代理,就会使事务无效,特别是,由于MTS类型的远程数据模块可以与状态无关,这时候如果绕过MTS代理,有可能导致程序崩溃。
  同样,单实例模式的CORBA数据模块也是与状态无关的,也存在着上述的问题。
  为了使客户程序能够方便地访问MTS或CORBA数据模块(因为这时候没有IProvider接口),您必须对远程数据模块的接口进行扩展,添加一些方法让客户程序调用。
  MTS或CORBA数据模块的接口都是从IDataBroker继承下来的。要向接口中加入新的属性或方法,您可以有两种操作方式:
  一是使用“Edit”菜单上的“Add to Interface”命令,弹出“Add to Interface”对话框,如图3.6所示。
  图3.6 “Add to Interface”对话框
  在“Declaration”框内键入属性或方法的声明,单击OK按钮,Delphi 4就会把属性或方法加入到远程数据模块的接口中。
  二是使用“View”菜单上的“Type Library”命令打开类型库编辑器,如图3.7所示。
  在类型库编辑器中选择接口节点,例如图3.7中的IXXH,然后单击工具栏 上的“New Method”按钮或“New Property”按钮。加入了一个新的成员后,要设置有关属性。要说明的是,对于CORBA类型的远程数据模块的接口来说,许多属性是无效的。
  对于基于COM的远程数据模块(TRemoteDataModule或TMTSDataModule)来说,新的成员将出现在接口的实现单元和类型库的描述文件中。
  对于基于CORBA的远程数据模块(TCorbaDataModule)来说,新的成员除了加到接口的实现单元中外,还会自动生成一个_TLB单元。如果客户程序需要访问基于CORBA远程数据模块,必须引用这个单元。另外,您可以让类型库编辑器生成IDL脚本,然后用Interface Repository和Object Activation Daemon来注册接口。
  客户程序可以通过MIDAS连接构件的AppServer属性获取远程数据模块的接口,然后通过接口调用远程数据模块的方法。
  注意:如果使用MTS的话,每一个加入到接口中的方法必须在最后调用SetComplete,告诉MTS事务可以结束了。例如,下面的GetCustomerRecords函数用于获取记录:
Function TMyRemoteDataModule.GetCustomerRecords(MetaData: Boolean; outRecsOut: Integer):OleVariant;
Begin
Try
If MetaData then Result := CustomerProvider.GetRecords(0, RecsOut);
Else Result := CustomerProvider.GetRecords(-1, RecsOut);
SetComplete;
ExceptSetAbort;
End;
End;
在客户端,可以这样调用GetCustomerRecords函数:
ClientDataSet1.Data := CorbaConnection1.AppServer.GetCustomerRecords(False, RecsOut);
再举个例子,下面的ApplyCustomerUpdates函数用于申请更新数据:
Function TMyRemoteDataModule.ApplyCustomerUpdates(Delta: OleVarant; MaxErrors: Integer; outErrorCount: Integer); OleVariant;
Begin
Try
Result := CustomerProvider.ApplyUpdates(Delta, MaxErrors, ErrorCount);
SetComplete;
ExceptSetAbort;
End;
End;
  在客户端,可以这样调用ApplyCustomerUpdates函数:
With ClientDataSet1 Do
Begin
CheckBrowseMode;
If ChangeCount > 0 then
Reconcile(MyConnectionComponent.AppServer.ApplyCustomerUpdates(Delta,MaxErrors, ErrCount));

上一页  [1] [2] [3] [4]  下一页

打印本文 打印本文 关闭窗口 关闭窗口