这应该不算是什么技巧,估计有数据库方面程序的DELPHI程序员都知道;本来没有写想到 需要把它写成一篇贴子,但前不久看个别刚入门的兄弟的代码时。才发展他们还在刀 耕火种,为此才想把它写出来算是对入门的兄弟们的一点帮助,让大侠们见笑了; 我们都知道TCLIENTDATASET有把对它其中的数据所做的修改记录下来的功能;如果需要还可以将 修改回复到以前的某个状态,而且结合DATASETPROVIDER还可能自动完成改动到SQL映射,将改动成批提交到数据库中 这个特性对于写数据库程序来说非常有用。其实我所说的保存CLIENTDATASET的数据也是利用这个特性来实现的; 代码很简单: unit uCDSSave; interface uses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs,Provider,DBClient,DB,Variants; type TSaveCDS = class (TObject) private Fileds: TStrings; FProvider: TDataSetProvider; procedure ReconcileError(DataSet: TCustomClientDataSet; E: EReconcileError; UpdateKind: TUpdateKind; var Action: TReconcileAction); public constructor Create; destructor Destroy; override; procedure CDSSave(CDS:TClientDataSet;TableName,keyFiled, NoSaveFileds:String;KeyUpdate:Boolean=False); procedure DataSetProviderUpdateData(Sender: TObject;DataSet: TCustomClientDataSet); procedure SetCDS(KeyFiled,NoSavefields:string;KeyUpdate:Boolean=False); property Provider: TDataSetProvider read FProvider write FProvider; end; implementation { *********************************** TSaveCDS *********************************** } constructor TSaveCDS.Create; begin inherited Create; Fileds:=TStringList.Create; FProvider:=TDataSetProvider.Create(nil); FProvider.UpdateMode:= upWhereKeyOnly ; FProvider.Options:=FProvider.Options+[poAllowMultiRecordUpdates]; end; destructor TSaveCDS.Destroy; begin FreeAndNil(FProvider); FreeAndNil(Fileds); inherited Destroy; end; procedure TSaveCDS.CDSSave(CDS:TClientDataSet;TableName,keyFiled, NoSaveFileds:String;KeyUpdate:Boolean=False); var ErrCount: Integer; begin CDS.CheckBrowseMode; if CDS.ChangeCount<1 then Exit; CDS.OnReconcileError:= ReconcileError; //调用保存前客户代码应该先调用SetCDS指定保存的信息; // SetCDS(CDS,keyFiled,NoSaveFileds,KeyUpdate); // FProvider.ApplyUpdates(CDS.Delta,0,ErrCount) try CDS.Reconcile(FProvider.ApplyUpdates(CDS.Delta,0,ErrCount)); finally CDS.OnReconcileError:=nil; end; end; procedure TSaveCDS.DataSetProviderUpdateData(Sender: TObject;DataSet: TCustomClientDataSet); var i: Integer; v: OLEVariant; KeyUpdate: Boolean; begin varClear(v); V:=DataSet.GetOptionalParam(''''KEYUPDATE''''); if not (VarIsNull(V) or VarIsClear(V)) then KeyUpdate:=true else KeyUpdate:=False; V:=DataSet.GetOptionalParam(''''KEYFILED''''); if Assigned(DataSet.FindField(VarToStr(V))) then if KeyUpdate then DataSet.FindField(VarToStr(V)).ProviderFlags:=[pfInupdate,pfinKey] else DataSet.FindField(VarToStr(V)).ProviderFlags:=[pfinKey]; V:=DataSet.GetOptionalParam(''''NOSAVEFILEDS''''); if (VarIsNull(V) or VarIsClear(V)) then Exit; Fileds.Clear; Fileds.Text:=VarToStr(V); if Fileds.Count<1 then Exit; //将不保存的传过来 ,在这个事件中解析 //这只是一种方法而已,当然也有其他的方式,如用BYTE数组打包到数据包中;请读者自已考虑实现了; for i:=1 to Fileds.Count-1 do if Assigned(DataSet.FieldByName(Fileds[i])) then DataSet.FieldByName(Fileds[i]).ProviderFlags:=[]; end; procedure TSaveCDS.ReconcileError(DataSet: TCustomClientDataSet; E: EReconcileError; UpdateKind: TUpdateKind; var Action: TReconcileAction); begin Raise E; //只是简单的抛出例外,如有自己的处理请自己实现了; end; procedure TSaveCDS.SetCDS(KeyFiled,NoSavefields:string;KeyUpdate:Boolean=False); begin CDS.SetOptionalParam(''''TABLE_NAME'''' ,TableName,true);//指定要存入的表名; CDS.SetOptionalParam(''''KEYFILED'''' ,keyFiled,true);//指定要主建名; CDS.SetOptionalParam(''''NOSAVEFILEDS'''' ,NoSaveFileds,true);//指定不要存入的字段列表; if KeyUpDate then CDS.SetOptionalParam(''''KEYUPDATE'''' ,1,true);// 指定主健是否要更新字段列表 end; end. 这段代码不一定是最优的实现,主要是想给出一个思路,用时可以根据实际情况改动,例如:本来程序是用的MIDAS来实现,则 就可以直接用CLIENTDATASET连接中间的TDATASETPROVIDER来实现;有一点要特别说明的是我发现在D5时,在前端的CLIENTDATASET 中直接设定各个字段的PROVIDERFLAG后不会被打包到Delta传给TDATASETPROVIDER;D7中好象也有此问题,不知是我看错了,还是D的BUG; 否则就不用那么麻烦自已写代码来处理了;
[Delphi程序]Delphi的ClientDataSet使用模糊过滤
|