经典问题:三层结构ado少量多次取数据到客户端(up者有分) (300分)

  • 主题发起人 主题发起人 micro73
  • 开始时间 开始时间
M

micro73

Unregistered / Unconfirmed
GUEST, unregistred user!
我正用win98+delphi6.0 update2+ado开发三层结构数据管理系统,后台数据库为MS SQL2000。
仿照李sir《系统篇》中第5章“鱼与熊掌兼得法”中的例子(他用的是BDE),想用ADO实现
少量多次取数据到客户端,出现数据表未active的问题。代码如下:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, ExtCtrls, DBCtrls, DB, DBClient, MConnect,
SConnect, StdCtrls;

type
TForm1 = class(TForm)
SocketConnection1: TSocketConnection;
ClientDataSet1: TClientDataSet;
DataSource1: TDataSource;
DBNavigator1: TDBNavigator;
DBGrid1: TDBGrid;
Button1: TButton;
procedure ClientDataSet1BeforeGetRecords(Sender: TObject;
var OwnerData: OleVariant);
procedure Button1Click(Sender: TObject);
procedure ClientDataSet1AfterGetRecords(Sender: TObject;
var OwnerData: OleVariant);
procedure FormActivate(Sender: TObject);
procedure FormDeactivate(Sender: TObject);
private
bTrueEof:Boolean;
vOwnerData:OleVariant;
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }


procedure TForm1.ClientDataSet1BeforeGetRecords(Sender: TObject;
var OwnerData: OleVariant);
begin
OwnerData:=vOwnerData;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
abk:TBookMark;
begin
try
if (not bTrueEof) then
begin
try abk:=ClientDataSet1.GetBookmark;
if (ClientDataSet1.GetNextPacket < ClientDataSet1.PacketRecords) then
bTrueEof:=True;
ClientDataSet1.Last;
vOwnerData:=ClientDataSet1.FieldByName('工作编号').Value;
ClientDataSet1.GotoBookmark(abk);
ClientDataSet1.Next;
finally
ClientDataSet1.FreeBookmark(abk);
end;
end;
Except
on Exception do;
end;
end;

procedure TForm1.ClientDataSet1AfterGetRecords(Sender: TObject;
var OwnerData: OleVariant);
var
CurRecord: TBookMark;
begin
try
with Sender as TClientDataSet do
begin
CurRecord := GetBookmark;
try
Last;
vOwnerData := FieldByName('工作编号').Value; //出问题行
GotoBookmark(CurRecord); { return to current record }
finally
FreeBookmark(CurRecord);
end;
end;
except
on Exception do;
end;
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
SocketConnection1.Connected := True;
ClientDataSet1.Active := True;
end;

procedure TForm1.FormDeactivate(Sender: TObject);
begin
ClientDataSet1.Active := False;
SocketConnection1.Connected := False;
end;

end.

各位大虾帮我看看。实现过程:
1.请先在服务器端放入一个TAdoConnection连接到ms sql2000数据库,一个TADODataSet(CacheSize:1000,
采用异步存取),一个TDataSetProvider([poAllowCommandText],ResolveToDataSet:True)
2.客户端一个TSocketConnection,一个TClientDataSet(FetchOnDemand:false,PackRecord:10,CommandText:
select * from 数据表名), 一个TDataSource,一个TDBGrid,一个TDbNavigator,一个TButton),将代码
copy入客户端程序即可。
 
服务器端代码:
procedure TtestServer.DataSetProvider1BeforeGetRecords(Sender: TObject;
var OwnerData: OleVariant);
begin
with Sender as TDataSetProvider do
begin
DataSet.Open;
DataSet.Locate('工作编号', OwnerData, []);
end;
end;
 
请帮忙up
 
据说客户端用socketconnection的时候 服务端须先运行delphi/bin/scktsrvr.exe
 
to PLWang:
兄弟,我的程序可以正确取数,跳过出错的地方可以继续运行,你说是不是没运行
scktsrvr.exe的原因?
 
请各位DFW在回答问题前先运行一下程序,分不够可以再加。
 
我也想知道,顶!
 
在这个函数 ClientDataSet1AfterGetRecords 调用时,有问题:
try
if Not ClientDataSet1.Active then Exit; // 加入:如果未启动,则退出
with Sender as TClientDataSet do
...........

且在 FormActivate 事件中为考虑:你是用 CommandText 方法,
ClientDataSet1.Activate 为 True 时,要传递命令行:如:Select * from Tb;
及:(按照你的说法,已经能运行,可能你在服务器端已经设定了 CommandText);
SocketConnection1.Activate := True;
ClientDataSet1.CommandText := 'Select * from Tb';
ClientDateSet1.Activate := True;

在调用时可以发现:ClientDataSet1AfterGetRecords 事件发生在 ClientDataSet1AfterOpen
事件前,也就是说你在 ClientDataSet1.Open 后,产生的事件次序为:
AfterGetRecords, AfterOpen; 在AfterOpen 事件中,ClientDataSet1 的 Active 才真正为 True;
所以,你要将 AfterGetRecords 中的代码写在 AfterOpen 事件中。
 
在出问题行之行CLIENTDATA。ACTIVE:=TRUE一次试试看!
 
to AsStone:
你可能没有运行我的程序,你说的事件执行顺序我不知道是不是那样。但我是在客户端
传递CommandText的,而且我把SQL语句直接放在服务器端也是出现同样的现象。
另外,你说要将 AfterGetRecords 中的代码写在 AfterOpen 事件中,你仔细想一想就
会知道根本行不通的,而且同样的代码李sir的BDE程序都可以正常执行,我换成Ado就不行。
具体的你可以参考李sir的《系统篇》中带的第5章中的源代码。

to 游向明:
你可能没仔细看我的程序,我在窗体Active时就已经执行了CLIENTDATA1.ACTIVE:=TRUE
to all:
我相信很多人都需要解决这样一个问题,大家努力想想。在回答前请先运行一下程序!
 
我说的是正确的,而且运行过程序,绝对正确!
你说的事件执行次序,你可以在 AfterGetRecords,和AfterOpen 中,加入断点,就可知!
你为什么不按我的方式改一下程序呢?你会发现你是错误的。
 
请大家踊跃跟贴
 
你在 AfterGetRecords 事件最开始,加断点,检验ClientDataSet1.Active 是否为 True,就知道了。
 
to AsStone:
很感谢你的回复,我知道你的方法从程序能否运行的角度来说是正确的。但你却忽略了
很重的一点:那就是我的目的是少量多次取数。其实现的原理就是为减少网络的Pooling,
客户端并不是将所有数据一次性取到客户端,而是取得一定数目记录(PackRecord)后,
服务器端便不再维护客户端的数据,也就是我们所说的无状态对象。例如当我们设定了
PackRecord的数目为10,那么当客户端需要浏览第11条记录时,TClientDataSet会向它连
结的TDataSetProvider要求传送下一个数据包。但是,由于我们用的是无状态对象,就需
要由客户端传递一个正确的当标位置,再让TDataSetProvider搜寻到正确的数据,然后传
回下一个正确的数据包。
在我的程序中(其实用的是李SIR的程序),当客户端调用GetNextPacket向服务器端
取得数据时,在TClientDataSet会触发BeforeGetRecords事件处理函数。在BeforeGetRecords
中把上一次存取的数据包的最后一笔键值传递给TDataSetProvider组件。当新的数据包传送
到客户端后,TClientDataSet的AfterGetRecords事件处理函数便会触发,在AfterGetRecords
中储存最新数据包的最后一笔键值,以便下一次调用GetNextPacket时使用来正确的设定
TDataSetProvider的光标位置。
请你仔细想想,你的方法能达到我的要求吗?麻烦你再帮我看看,等问题解决了马上给分。
 
后退
顶部