cds理论
http://gang4415.blog.51cto.com/225775/273481
问题.500表,难道用500个DataSetProvider?
回复:
1楼:是的,成对出现,除非你不是用DataSetProvider做连接的。不过一般要看ClientDataSet最多时会显示几个,DataSetProvider就有几个太多的话,可以动态创建,用TList管理。
2楼:可以只用一个。使用通用的参数,比如参数1指定库名,参数2指明表的名字,参数3编码组合实际的参数。中间层解包后在query的beforeopen事件里动态生成相应的sql语句。我们公司采用的也是这种方案。
2. Delphi做三层开发时,很多人都会在客户端放一个TClientDataSet,中间层远程数据模块就对应放一个TDataSetProvider, 然后再连起来.其实这种方法很烦琐,而且程序痈肿不甘,不好维护.我们都知道TClientDataSet的Delta属性记录了数据的所有修改,应用它 我们就可以方便的实现一个单表更新的通用方法.?
回复:
中间层添加一个通用的更新方法,就叫ApplyUpdates。
方法定义如下: function ApplyUpdates(const UpdateTable:String;Delta:Variant;out err:String):Boolean;
参数UpdateTable是指要更新的表名,Delta是指传过来的TClientDataSet的Delta属性,如果更新错误err返回错误的内 容(实现这个方法思路,在DataModule上放一个TdataSetProvider—Query--Connection首先开始一个事物Conn.BeginTrans;
格式化语句填入表名:sqlstr:=Format('select * from %s where 1=0',[UpdateTable])后传入Query打开,取得这个表的表结构。提交修改的数据Provider.ApplyUpdates(Delta,-1,ErrCount);并对Reuslt赋值Result:=ErrCount=0;根据Reuslt判断事物是提交还是回滚。)
到此,通用的更新方法已经完成了.不过客户端的ClientDataSet还不能查询显示数据,因此,还要写一个查询方法:
??? function QuerySQL(const sqlstr:string;out Data:Variant;out err:String):Boolean;
??? 参数sqlstr就是要持行的查询语句,Data返回查询结果,错误时err返回错误消息
到这里,中间层的代码已经完了,客户端的调用就简单了.比如客户端有个数据模块DM,上面放一个DcomConnection或者 SocketConnection,名叫Conn.客户端上增删改完后,这时的数据还在客户端的内存里,想保存到远程的中间层服务器就要用到我们刚才的方法了比如
var err:string;
begin
if cds.ChangeCount=0 then exit;//数据没改变就不用提交了;
if Dm.Conn.AppServer.ApplyUpdates('xxx',cds.Delta,err) then//xxx就是表名了
? cds.MergeChangeLog;//合并所有改变的数据
end else MessageBox(self.handle,pchar('保存出错:'+err),'错误',MB_OK+MB_ICONERROR);
end;
到此,这篇文章也讲完了.用这个方法,那些单表的基础数据更新还可以写成一个祖先类,只要加一个取得更新表名的虚方法,比如:function TableName:string;virtual;然后其后代只要override这个方法,返回各自的表名,其他的一句代码都不用写.
有个小问题是,这个表的主键的生成问题,这里我们不能用自增主键,要自己自己生成主键,这样你还得在中间层写一个中间层生成主键的方法,
问题
慎用TclientDataSet.Data, 当我们调用cdsxxx.data := anyData,它会把值赋给FSavedPacket。所以此时FSavedPacket已经<>nil了,而且从源码上看,TClientDataset在close的时候并没有把FSavedPacket清掉。所以下次Open的时候,其实这个Open是不会去取数据的。
解决这个问题的办法是自己派生一个新的ClientDataset
TMyClientDataSet = class(TClientDataset)
protected
?? procedure CloseCursor; override;?
TMyClientDataSet.CloseCursor;
begin
? inherited CloseCursor;
? Data := null; //此处清空FSavedPacket
end;
问题:TClientDataSet加上TDataSetProvider的数据保存问题?
前台:D7或D2005; DBExpress连接组件: ?TSqlConnection,TSqlQuery ,TDataSetProvider,TClientDataSet;
后台:SQLServer2000
更新数据的命令:DataSetProvider.ApplyUpdates(ClientDataSet.Delta , -1 , ErrorCount);
设置DataSetProvider.ResolvetoDataSet:= True,用以触发BeforeUpdateRecord事件处理函数; 更新数据的命令写在BeforeUpdateRecord事件中。问题: 1新数据前必须要断开连接,否则就有可能出错。错误的提示意思是不能再开启事务了,其实根本就没有手动开启事务,只有BeforeUpdateRecord自动开启的事务。2因为更新每一个ClientDataSet之前都要断开连接,因此更新多个ClientDataSet时无法进行事务处理。
TDBGrid->TDataSource->TClientDataSet->TDataSetProvider->TSQLQuery->TSQLConncetion
要知道,TClientDataSet的CommandText属性是不可写的,要变换不同的SQL语句进行查询,就修改TSQLQuery的SQL.Text属性,然后用TClientDataSet的Open方法执行SQL语句。
ClientDataSet在设计时add all fields
问题:一直以来字典类数据都是用DBExpress+ClientDataSet来出来的.但知道最近才发现在处理SQL SERVER2000数据库时对字符型字段,保存的数据末尾有'\0'(ASCII 0)的存在.也就是说,如果保存的是'ABCD',那么实际数据库字段长度不是4,而是5.这个问题该如何解决呢?不是空格,所以trim无效.实际上我测试过ClientDataSet.Filed[n].AsString的长度,是正常的.只是提交数据库后才出问题.
回复:经测试,和DBExpress的数据驱动程序有关.连接ORACLE时没有该问题,改用ADO连接SQLSERVER也没有该问题.
问题:如何获得ClientDataSet执行的SQL语句。
回复:SQL语句不是由ClientDataSet解悉的,可能是由DataSetProvider解悉,也可能是由ADO解悉,前者应该可以取得,后者就难了.但这些都是在ApplyUpdates后发生的,不過你可以DataSetProvider1BeforeUpdateRecord(Sender: ? TObject;?
? ? SourceDS: ? TDataSet; ? DeltaDS: ? TCustomClientDataSet;?UpdateKind: ? TUpdateKind; ? var ? Applied: ? Boolean);這個事件里?用DeltaDS來看個個字段的值﹐以及各種狀態啊
问题:
利用Adoquery ? ,clientdataset ? ,datasetprovidor ? 更新?
datasetprovidor ? 設置?
resolvetodataset ? =true;?
updatemode ? =upwhereall?
Adoquery ? 設置?
cursorlocation ? =cluseclient?
cursortype ? =ctstatic?
locktype ? =ltbatchoptmistic ? or ? / ? ltoptmistic?
以前post ? ,再applyupdates(-1),有時正常,有時出現,record ? not ? found ? ? or ? change ? by ? another ? user ? 就裝了個D6_UPD2_EN.EXE,ADO26_TRA.exe?
clientdataset中有一自動增長id ? (sql ? server ? table ? A ? ? ,id是主關鍵字) ? 再數據更新applyupdates(-1),或applyupdates(-1)出現key ? violation ? 錯誤信息!,單單自動post也出現key ? violation ? 錯誤信息!,?
但另做一個PROJECT ? 利用Adoquery ? ,clientdataset ? ,datasetprovidor,同樣設置, ? applyupdates(0)又成功更新,id自動增長,怎麼解決!請大家幫幫手,謝謝?
回复:自增量字段你是不用作处理的,你在新增时不要显式的对该字段设值,那是数据库作的工作。
提问:我用 ? SQLConnection-> DataSetProvider-> ClientDataSet ? 的结构连接IB数据库,在程序里面给ClientDataSet-> CommandText赋值再Open,就会发生异常,说是“CommandText ? changes ? are ? not ? allowed”。不知道是为什么,高手指点一下啦
回复:DataSetProvider的Options属性里,poAllowCommandText值要为true
问题:执行ClientDataSet1.Append ? 添加完数据后,保存的时候就出现“Record ? not ? found ? or ? changed ? by ? another ? user”的错误,应当如何解决??保存的代码为: ? ClientDataSet1.ApplyUpdates(-1);
回复:ClientDataSet应用更新时,服务端控件先要检索该记录:select ? * ? from ? 表名 ? where ? 字段1= '更新前内容 ' ? and ? 字段2= '更新前内容 ' ? and ? 字段3= '更新前内容 ' ? and ? …… ? ? 所有字段都作为条件。?
该错误出现的情况有:?
1、该记录在提交更新时被他人修改过?
2、字段中有长日期类型?
解决方法:?
不用ClientDataSet更新,通过SQL语句更新或者设置服务端的控件的字段属性,把主键字段的ProviderFlags的pfinKey属性设为true,这样ClientDataSet更新时服务端检索记录时的条件仅包含该字段而不是所有字段?
当然,这样的话如果在提交之前该记录被他人修改过(不改主键字段)的话,你仍可更新该记录,而不会有你看到的错误提示。
问题:我想清空内存里的数据,用的是 ? clientdataset.close ??帮助里说.close是切断客户端与服务器端的联系而已;?有什么办法可以切断他们的联系,并且清空内存的数据呢?
回复:clientdataset.freedata?我记得好像有这个;请问FREEDATA执行的时机??好像是EMPTYDATA
ClientDataSet字段不能进行编辑时的解决方法
ClientDataSet字段不能进行编辑时的解决方法:
procedure ModifyClientDataSet(const YesOrNot: Boolean;
? cs : TClientDataSet);
var
? i : Integer;
begin
? // 当 YesOrNot 为 true 时为只读? 为false 是可进行修改
? for i := 0 to cs.FieldCount -1 do
? begin
??? cs.Fields[i].ReadOnly := YesOrNot;
? end;
end;
当ClientDataSet 所对应的不是真实存在的字段时,就会出现不能进行编辑的情况。
比如是通过 '' as 字段1 , 这时 字段1 在运行时是不能编辑的。
?如果ClientDataSet是能过动态创建的,或都是assigned其他的ClientDataSet的结构,
需要在 ClientDataSet.CreateDataSet 之后再调用此方法
ClientDataSet控键的动态 Assign Local Data 解决方案
在设计模式下,可以用鼠标右键Assign Local Data加载Table或者Query数据,
但在代码里实现却无从下手. Google了一段时间,找到一个解决办法.
1. var??
2. ??pv:TProvider;??
3. begin??
4. ??pv:=TProvider.Create(nil);??
5. ??pv.DataSet:=adoquery1;??
6. ??ClientDataSet1.Data:=pv.Data;??
7. ??freeandnil(pv);??
8. end;?
对Raptor的一点补充:?
其一,在三层结构中使用SQL,违背了三层结构的宗旨,不如回家去用两层。?
其二,与其瞎猜,不如实际动手证实一下,如果你使用的数据库是SQL ? SERVER,那么使用事件跟踪器一目了然。?
其三,我同意使用upWhereKey,但其实根本不需要自己去设,当然做法需要不一样,原理是这样从数据集的元数据中
? ? ? ? ? ? 可以完全知道,何必多此一举