ADOQuery AfterScroll 没有触发一条记录/没有记录
ADOQuery AfterScroll not Triggered with One Record / No Record
我在表单上有一个 MasterQry 和一个 SlaveQry。
MasterQry 是这样的:
select * from Header where Active = 1
。
在它的 AfterScroll 事件中,我有以下内容:
select * from Slave where HeadID=:Header.ID
现在如果我这样做:
if MasterQry.Active then MasterQry.Close;
MasterQry.Open;
如果我有不止一条记录,这将完美运行,但如果我只有一条,则无法运行。
即使我这样做MasterQry.First;
也没有任何反应。
如果我尝试 MasterQry.AfterScroll(MasterQry)
我会遇到访问冲突。
我正在重构我的代码并试图使其更紧凑,因为我做了很多打开关闭定位 ID(需要刷新数据以获得实际状态,是否已锁定等)并且我这样做了:
function RefreshQuery(AQuery : TADOQuery; ID : integer) : boolean ; overload;
var AfterOpen,AfterScroll : TDataSetNotifyEvent;
begin
result:=false;
AfterOpen := AQuery.AfterOpen;
AfterScroll := AQuery.AfterScroll;
AQuery.AfterOpen:=nil;
AQuery.AfterScroll:=nil;
if AQuery.Active then AQuery.Close;
AQuery.Open;
if not AQuery.Locate('id', ID, []) then
result:=false
else
result:=true;
AQuery.AfterOpen:=AfterOpen;
AQuery.AfterScroll:=AfterScroll;
if Assigned(AQuery.AfterScroll) then
AQuery.AfterScroll(AQuery);
end;
请注意此代码不是通用的,但非常适合我的需要。我注意到这里的 AfterScroll 事件被触发,即使我在 MasterQry 中只有一个记录,或者即使我根本没有。我真的很高兴,我多次测试它并提供了正确的结果。
我检查了 TADOQuery.First 和 TADOQuery.Locate 程序,它们都有 DoAfterScroll 但它不是由 One Record 或 No Record 触发的。 (SlaveQry 在不希望的状态下打开)
为此我在谷歌上搜索了很多,但找不到原因。
我的问题是:为什么这样做有效?为什么 AfterScroll 会以 One or No Record 触发。
谢谢。
更新
我只能用 Microsoft SQL 重现这个。所以为了测试这个你需要。两个表 .
在MasterTable中添加两条记录(ID、Text、Active)
1 前 1
2秒1
在SlaveTable中添加两条或多条记录(ID,HeadID,Text)
1,1,First-1
2,1,First-2
3,2,第二-1
4,2,第二-2
现在放在表格上 1 ADOConnection 2 ADOQueries .
在 MainQuery 中您有以下文本
Select * from MasterTable where Active=1
在 SlaveQuery 中你有以下文本
select * from SlaveTable where HeadID=:HeadID
在 MainQuery.BeforeOpen 你有这个 :
MainQuery.AfterScroll:=nil;
在 MainQuery.AfterScroll 你有这个:
if SlaveQuery.Active then SlaveQuery.Close;
SlaveQuery.Parameters.ParamByName('HeadID').Value:=MainQueryID.Value;
SlaveQuery.Open;
在 MainQuery.AfterOpen 你有这个 :
MainQuery.AfterScroll:=MainQueryAfterScroll;
向此表单添加一个 按钮 :
Button1Click 事件包含以下内容:
if MasterQuery.Active then MasterQuery.Close;
MasterQuery.Open;
因此,如果您现在将网格附加到两个查询,您可以看到它完美地遵循。
在不关闭程序的情况下,进入 SQL 服务器管理器并 运行 以下更新语句:
update MasterTable set Active=0
再次按下窗体上的 Button1 :
MasterQuery 为空,SlaveQuery 处于上次打开状态。
为了解决这个问题,您需要按如下方式更改 Button1Click :
var AfterOpen,AfterScroll : TDataSetNotifyEvent;
begin
AfterOpen := AQuery.AfterOpen;
AfterScroll := AQuery.AfterScroll;
AQuery.AfterOpen:=nil;
AQuery.AfterScroll:=nil;
if AQuery.Active then AQuery.Close;
AQuery.Open;
AQuery.AfterOpen:=AfterOpen;
AQuery.AfterScroll:=AfterScroll;
if Assigned(AQuery.AfterScroll) then
AQuery.AfterScroll(AQuery);
end;
现在可以了。我不知道为什么,因为 MasterQuery.First 应该触发 DoAfterScroll 但什么也没发生。
似乎将 AfterScroll 设置为 nil 然后再返回以某种方式触发 AfterScroll,即使它有 1 条记录或为空也是如此。
正如我在评论中所说,RefreshQuery
中的大部分代码都不是必需的,
因为链接 Master->Detail 数据集应该 "just work"。事实上,您的 RefreshQuery
根本不需要。
我创建了一个基于你的主从 table 的最小项目,只是通过删除组件
从托盘中,将它们连接起来并仅添加下面 Form1.FormCreate 中的代码。这
从网格的内容正确地跟踪主网格,包括以下情况
没有匹配的 Slave 记录,即 Slave 网格显示为空。请注意,没有任何需要的数据事件,即没有 AfterScroll
和 Locate
等调用。
type
TForm1 = class(TForm)
dsMaster: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
DBGrid2: TDBGrid;
DataSource2: TDataSource;
DBNavigator2: TDBNavigator;
ADOConnection1: TADOConnection;
qMaster: TADOQuery;
qSlave: TADOQuery;
qSlaveID: TIntegerField;
qSlaveHeaderID: TIntegerField;
qSlaveAText: TWideStringField;
procedure FormCreate(Sender: TObject);
public
end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
begin
qMaster.SQL.Text := 'select * from mastertable';
qSlave.DataSource := dsMaster;
qSlave.SQL.Text := 'select * from slavetable where headerid = :id';
// NOTE: because the DataSource property of qSlave is set to dsMaster,
// the ` = :id` tells the Ado run-time code to get the value of the
// ID field in the qMaster table.
qMaster.Open;
qSlave.Open;
end;
如果你想刷新 Master 或 Slave table 以防其他用户更改其中的记录,你可以这样做:
procedure TForm1.Button1Click(Sender: TObject);
begin
qMaster.Refresh;
end;
但请注意 table 需要在 Sql 服务器上正确设置。只要将 ID 字段设置为主键,and/or 在服务器上设置了唯一索引,对 Refresh
的调用应该可以正常工作,但如果没有,您将得到一条错误消息 "Insufficient key information for updating or refreshing." 您当然可以在计时器上进行刷新(但不要过于频繁地调用它,即每隔几秒多次)。
我在表单上有一个 MasterQry 和一个 SlaveQry。 MasterQry 是这样的:
select * from Header where Active = 1
。
在它的 AfterScroll 事件中,我有以下内容:
select * from Slave where HeadID=:Header.ID
现在如果我这样做:
if MasterQry.Active then MasterQry.Close;
MasterQry.Open;
如果我有不止一条记录,这将完美运行,但如果我只有一条,则无法运行。
即使我这样做MasterQry.First;
也没有任何反应。
如果我尝试 MasterQry.AfterScroll(MasterQry)
我会遇到访问冲突。
我正在重构我的代码并试图使其更紧凑,因为我做了很多打开关闭定位 ID(需要刷新数据以获得实际状态,是否已锁定等)并且我这样做了:
function RefreshQuery(AQuery : TADOQuery; ID : integer) : boolean ; overload;
var AfterOpen,AfterScroll : TDataSetNotifyEvent;
begin
result:=false;
AfterOpen := AQuery.AfterOpen;
AfterScroll := AQuery.AfterScroll;
AQuery.AfterOpen:=nil;
AQuery.AfterScroll:=nil;
if AQuery.Active then AQuery.Close;
AQuery.Open;
if not AQuery.Locate('id', ID, []) then
result:=false
else
result:=true;
AQuery.AfterOpen:=AfterOpen;
AQuery.AfterScroll:=AfterScroll;
if Assigned(AQuery.AfterScroll) then
AQuery.AfterScroll(AQuery);
end;
请注意此代码不是通用的,但非常适合我的需要。我注意到这里的 AfterScroll 事件被触发,即使我在 MasterQry 中只有一个记录,或者即使我根本没有。我真的很高兴,我多次测试它并提供了正确的结果。
我检查了 TADOQuery.First 和 TADOQuery.Locate 程序,它们都有 DoAfterScroll 但它不是由 One Record 或 No Record 触发的。 (SlaveQry 在不希望的状态下打开)
为此我在谷歌上搜索了很多,但找不到原因。
我的问题是:为什么这样做有效?为什么 AfterScroll 会以 One or No Record 触发。
谢谢。
更新
我只能用 Microsoft SQL 重现这个。所以为了测试这个你需要。两个表 .
在MasterTable中添加两条记录(ID、Text、Active)
1 前 1
2秒1
在SlaveTable中添加两条或多条记录(ID,HeadID,Text)
1,1,First-1
2,1,First-2
3,2,第二-1
4,2,第二-2
现在放在表格上 1 ADOConnection 2 ADOQueries .
在 MainQuery 中您有以下文本
Select * from MasterTable where Active=1
在 SlaveQuery 中你有以下文本
select * from SlaveTable where HeadID=:HeadID
在 MainQuery.BeforeOpen 你有这个 :
MainQuery.AfterScroll:=nil;
在 MainQuery.AfterScroll 你有这个:
if SlaveQuery.Active then SlaveQuery.Close;
SlaveQuery.Parameters.ParamByName('HeadID').Value:=MainQueryID.Value;
SlaveQuery.Open;
在 MainQuery.AfterOpen 你有这个 :
MainQuery.AfterScroll:=MainQueryAfterScroll;
向此表单添加一个 按钮 :
Button1Click 事件包含以下内容:
if MasterQuery.Active then MasterQuery.Close;
MasterQuery.Open;
因此,如果您现在将网格附加到两个查询,您可以看到它完美地遵循。
在不关闭程序的情况下,进入 SQL 服务器管理器并 运行 以下更新语句:
update MasterTable set Active=0
再次按下窗体上的 Button1 :
MasterQuery 为空,SlaveQuery 处于上次打开状态。
为了解决这个问题,您需要按如下方式更改 Button1Click :
var AfterOpen,AfterScroll : TDataSetNotifyEvent;
begin
AfterOpen := AQuery.AfterOpen;
AfterScroll := AQuery.AfterScroll;
AQuery.AfterOpen:=nil;
AQuery.AfterScroll:=nil;
if AQuery.Active then AQuery.Close;
AQuery.Open;
AQuery.AfterOpen:=AfterOpen;
AQuery.AfterScroll:=AfterScroll;
if Assigned(AQuery.AfterScroll) then
AQuery.AfterScroll(AQuery);
end;
现在可以了。我不知道为什么,因为 MasterQuery.First 应该触发 DoAfterScroll 但什么也没发生。 似乎将 AfterScroll 设置为 nil 然后再返回以某种方式触发 AfterScroll,即使它有 1 条记录或为空也是如此。
正如我在评论中所说,RefreshQuery
中的大部分代码都不是必需的,
因为链接 Master->Detail 数据集应该 "just work"。事实上,您的 RefreshQuery
根本不需要。
我创建了一个基于你的主从 table 的最小项目,只是通过删除组件
从托盘中,将它们连接起来并仅添加下面 Form1.FormCreate 中的代码。这
从网格的内容正确地跟踪主网格,包括以下情况
没有匹配的 Slave 记录,即 Slave 网格显示为空。请注意,没有任何需要的数据事件,即没有 AfterScroll
和 Locate
等调用。
type
TForm1 = class(TForm)
dsMaster: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
DBGrid2: TDBGrid;
DataSource2: TDataSource;
DBNavigator2: TDBNavigator;
ADOConnection1: TADOConnection;
qMaster: TADOQuery;
qSlave: TADOQuery;
qSlaveID: TIntegerField;
qSlaveHeaderID: TIntegerField;
qSlaveAText: TWideStringField;
procedure FormCreate(Sender: TObject);
public
end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
begin
qMaster.SQL.Text := 'select * from mastertable';
qSlave.DataSource := dsMaster;
qSlave.SQL.Text := 'select * from slavetable where headerid = :id';
// NOTE: because the DataSource property of qSlave is set to dsMaster,
// the ` = :id` tells the Ado run-time code to get the value of the
// ID field in the qMaster table.
qMaster.Open;
qSlave.Open;
end;
如果你想刷新 Master 或 Slave table 以防其他用户更改其中的记录,你可以这样做:
procedure TForm1.Button1Click(Sender: TObject);
begin
qMaster.Refresh;
end;
但请注意 table 需要在 Sql 服务器上正确设置。只要将 ID 字段设置为主键,and/or 在服务器上设置了唯一索引,对 Refresh
的调用应该可以正常工作,但如果没有,您将得到一条错误消息 "Insufficient key information for updating or refreshing." 您当然可以在计时器上进行刷新(但不要过于频繁地调用它,即每隔几秒多次)。