如果我们想提交更新,我们就需要TDSProviderConnection组件与服务端的TDataSetProvider关
联,因此我们不但可以读取数据也可以修改数据.
首先,我们需要修改服务端数据模块,现有的只是返回一个TDataSet,我们必须添加一个实际的
TDataSetProvider,确保将其从DataSnap服务端发布到客户端.所以,回到ServerMethodsUnitDemo
单元放一个TDataSetProvider组件,设置其DataSet属性为TSQLDataSet.应该将TDataSetProvider 重命名,如dspEmplyees;
现在,重新编译DataSnap服务,运行.然后修改客户端. 3.2.1. TDSPROVIDERCONNECTION 客户端
为检索到发布的TDataSetProvider组件需要修改一下DataSnap客户端.将TSQLServerMethod和
TDataSetProvider组件从客户端窗体删除,添加一个TDSProviderConnection组件.设置其
SQLConnection属性为TSQLConnection组件,连接到DataSnap服务.我们也需要设置一个
ServerClassname属性值(很不幸不能选择只能输入).现在只能手动输入TDSServerModule的名字, 这里是TServerMethods1.
在前一个例子中,TClientDataSet只设置了一个ProviderName属性.然而,使用 TDSProviderConnection组件,我们必须首先设置其RemoteServer属性为TDSproviderConnection
组件,然后设置ProviderName属性(这个属性还为原来设置的DataSetProvider1,现在设置为
dsEmployeer----在服务端发布的TDataSetProvider组件的名称). 在ProvideName属性的下拉框中显示dspEmployees选项(在服务端的ServerDataModule单元中发 布的名称).
现在我们可以设置TClientDataSet.Active为True,在设计时查看数据. 设置TClientDataSet.Active为True,同时将TSQLConnection.Connected置为True.注意在设计
时最好不要将这两个属性设置为True.首先,如果你在IDE中打开DataSnap客户端项目,将会试图连
接服务端,如果服务端没开启将失败.其次,如果在运行时启动应用程序,并且连接不可用,应用程
序将会抛出异常.从而应用程序不能使用本地数据,导致远程连接无效将不能使用应用程序.
最好的方式是使用菜单选项或按钮明确的设置TSQLConnection组件连接,TClientDataSet组件
进行获取数据.并提交包括username/password的信息,将在后面说明.现在确保 TClientDataSet.Active属性设置为False,同时TSQLConnection.Connected属性为False.在客户
端窗体中放一个按钮,在OnClick事件中明确的打开TClientDataSet. procedure TForm2.Button2Click(Sender: TObject); begin
ClientDataSet1.Open; end;
现在添加代码提交数据变更,保存回服务器. 3.2.2 数据库更新
有两种方法将数据修改保存会服务端:自动和手动.都是调用一下方法,但是会自动调用或手动
调用,各有优缺点.
对于自动方式,我们可以使用TClientDataSet的数据修改时触发的OnAfterInsert,OnAfterPost
和OnAfterDelete事件.在事件处理程序中,实现很简单,调用TClientDataSet的ApplyUpdates方法,
发送变更,将Delta包发送到服务端保存回数据库.
procedure TForm2.ClientDataSet1AfterPost(DataSet: TDataSet); begin
ClientDataSet1.ApplyUpdates(0); end;
如果发生了更新错误,将会触发TClientDataSet的OnReconcileError事件,更多信息见3.2.3
手动方式发生更新也是使用TClientDataSet的ApplyUpdates方法.但是这时方法不在
OnAfterInsert,OnAfterPost和OnAfterDelete事件中执行.而是我们添加一个按钮让用户显示的提交更 新.
procedure TForm2.btnUpdateClick(Sender: TObject); begin
ClientDataSet1.ApplyUpdates(0); end;
自动提交的好处当然用户不会忘记将变更保存回服务端.然而,缺点是无法提供Undo能力.一旦
提交数据就更新回了服务器.另外,如果使用手动提交,所有变更保存在客户端---TClientDataSet
组件的内存中.这样就允许用户Undo部分变更:使特定记录或全部记录放弃更新.点击更新按钮显
示调用ApplyUpdate方法.可能会导致用户忘记提交修改数据.我们应该在窗口关闭时添加代码检
查TClientDataSet中是否还有未提交数据(检查TClientDataSet.ChangeCount属性).
3.2.3. RECONCILE ERRORS
TClientDataSet.ApplyUpdates方法有一个参数:应用更新时允许发生的最大错误数量.如果有
两个客户端连接到了DataSnap服务端,获取Employees数据并同时修改了第一行数据.依据目前为
止我们的实现,两个客户端都会使用TClientDataSet的ApplyUpdates将数据变更到DataSnap服务
端.如果都将ApplyUpdates的参数MaxErrors设置为0,则第二个客户端的提交将会停止.第二个客
户端应该使用一个大于0的参数指定允许的错误/冲突数.然而,即使第二个客户端将MaxErrors设
置为-1(不管有多少错误发生都继续提交后面的更新记录),都不会提交被第一个用户更新过的记
录.换句话说,你需要执行一系列冲突处理来解决这些更新已经被更新的记录或列的冲突问题.
幸运的是,Delphi提供了一个很强大的对话框来处理这个问题.当在DataSnap客户端需要做一些
冲突处理时,都可以使用这个对话框(或自己实现,但最终都是处理冲突问题). 使用Delphi提供的功能,File.New.Other,在Delphi文件子目录中选择
Reconcile Error对话框 图标.
选中这个图标点击OK,保存为RecError.pas,加入到DataSnapClient项目.这个单元包括了定义
和实现更新错误对话框.来解决数据库更新错误.
ReconcileErrorForm窗体实例将按需要动态创建.那么如何使用这个特殊的ReconcileErrorForm窗体呢?
好,其实很简单.对于每个没有成功更新的记录,都会触发TClientDataSet的OnReconcileError事件.定义如 下:
procedure TForm2.ClientDataSet1ReconcileError(DataSet: TClientDataSet; E: EReconcileError; UpdateKind: TUpdateKind;
var Action: TReconcileAction);
这个事件处理程序与四个参数,第一个是抛出错误的TClientDataSet,第二个参数是引发错误冲
突的原因,第三个参数是更新类型UpdateKind(insert,delete,modify),第四个参数是你要如何处
理冲突.可以返回如下枚举类型值:
- raSkip:不更新这条记录,但在变更日志中保留未提交的变更,下次提交在试. -raAbort:取消记录冲突处理..
-raMerge:将更新记录与远程数据库记录合并,仅在客户端变更修改过的远程字段
-raCorrect:使用正确的值替换更新记录,这需要用户介入. -raCancel:对本记录的修改全部放弃.回到初始值状态.
-raRefresh:对本记录修改全部放弃,但重新加载当前数据库的记录值. 关于ReconcileErrorForm不需要考虑全部执行选项.只需要做两件事件.一,在DataSnap客户端主窗体中
引用错误处理对话框单元.二,在OnReconcileError事件中写一行代码调用ReconcileErrorForm单元中的
HandleReconcileError全局函数. HandleReconcileError函数也有四个同样的参数,只需要按顺序传递即可.
如下所示:
procedure TFrmClient.ClientDataSet1ReconcileError(DataSet: TClientDataSet;
E: EReconcileError; UpdateKind: TUpdateKind; var Action: TReconcileAction); begin
Action := HandleReconcileError(DataSet, UpdateKind, E) end;
3.2.4. 示范冲突错误
现在最大的问题是:实际工作中如何使用的冲突处理?为了测试,需要两个或更多DataSnap客户
端同时运行.为使用当前的客户端和服务端进行测试,需要执行如下步骤: -启动服务端应用程序
-启动第一个客户端应用程序,点击链接按钮,获取数据 -启动第二个客户端应用程序,点击链接按钮,获取数据 -使用第一个客户端应用程序,修改第一行数据的FirstName列 -使用第二个客户端应用程序,修改第一行数据的FirstName列 -在第一个客户端应用程序中点击更新按钮
-在第二个客户端应用程序中点击更新按钮,这时将会发生一个或多个错误.因为第一个应用程
序已经修改了同一行的同一个列.引起冲突. OnReconcileError被触发. -进入更新错误对话框,现在可以处理冲突(忽略(Abort),取消(Abort),合并(Merge),更正(Correct),取消
(Cancel),更新(Refresh)).测试一下Skip和Cancel的不同,及Correct,Refresh和Merge的不同.
Skip移动到下一行记录,忽略更新请求.但其更新将保留在更新日志中.Cancel也忽略更新请求
同时清除本记录所有以前的更新记录.