在我的应用程序中,我有不同的表单,它们使用相同的数据源(因此查询也相同),在一个公共数据模块中定义。问题是,有没有办法知道我打开了多少次特定查询?通过能够做到这一点,我可以避免关闭该查询,而无需在“其他任何地方”关闭它。
编辑:重要的是要提到我正在使用Delphi3,它不是一个查询,而是几个。
在我的应用程序中,我有不同的表单,它们使用相同的数据源(因此查询也相同),在一个公共数据模块中定义。问题是,有没有办法知道我打开了多少次特定查询?通过能够做到这一点,我可以避免关闭该查询,而无需在“其他任何地方”关闭它。
编辑:重要的是要提到我正在使用Delphi3,它不是一个查询,而是几个。
这个想法是使用 TDataSource 的DataLinks属性。
但是,由于它受到保护,您必须访问它。一个常见的技巧是创建一个假后代只是为了铸造:
type
TDataSourceHack = class(TDataSource);
然后你像这样使用它:
IsUsed := TDataSourceHack(DataSource1).DataLinks.Count > 0;
您可以使用类似 addref/release 的方法获得创意。只需在您的共享数据模块中创建一些函数和一个整数变量来发挥作用,并确保调用它们。部分代码如下:
TDMShared = class(tDataModule)
private
fQueryCount : integer; // set to 0 in constructor
public
function GetQuery : tDataset;
procedure CloseQuery;
end;
function TDMShared.GetQuery : tDataset;
begin
inc(fQueryCount);
if fQueryCount = 1 then
SharedDatsetQry.open;
Result := shareddatasetqry; // your shared dataset here
end;
procedure TDMShared.CloseQuery;
begin
dec(fQueryCount);
if fQueryCount <= 0 then
shareddatasetqry.close; // close only when no refs left.
end;
编辑:要对多个查询执行此操作,您需要一个容器来保存查询引用,以及一种操作它们的方法。一个 tList 很适合这个。如果您使用的是旧版本的 Delphi,您将需要对您的 TDataset 后代进行适当的更改,并创建一个 FreeAndNil 函数。我用于此的概念是维护您请求的所有查询的列表,并通过句柄来操作它们,该句柄实际上是列表中查询的索引。方法 FreeUnusedQueries 可以释放任何不再具有引用的对象...这也可以作为 close 查询方法的一部分来完成,但我将其分开以处理需要重新打开特定查询的情况。模块。
Procedure TDMShared.DataModuleCreate(Sender:tObject);
begin
dsList := tList.create;
end;
Function TDMShared.CreateQuery(aSql:String):integer;
var
ds : tAdoDataset;
begin
// create your dataset here, for this example using TADODataset
ds := tAdoDataset.create(nil); // self managed
ds.connection := database;
ds.commandtext := aSql;
ds.tag := 0;
Result := dsList.add(ds);
end;
function TDMShared.GetQuery( handle : integer ) : tDataset;
begin
result := nil;
if handle > dsList.count-1 then exit;
if dsList.Items[ handle ] = nil then exit; // handle already closed
result := tAdoDataset( dsList.items[ handle ]);
Inc(Result.tag);
if Result.Tag = 1 then
Result.Open;
end;
procedure TDMShared.CloseQuery( handle : integer );
var
ds : tAdoDataset;
begin
if handle > dsLIst.count-1 then exit;
ds := tAdoDataset( dsList.items[ handle ]);
dec(ds.Tag);
if ds.Tag <= 0 then
ds.close;
end;
procedure TDMShared.FreeUnusedQueries;
var
ds : tAdoDataset;
ix : integer;
begin
for ix := 0 to dsList.Count - 1 do
begin
ds := tAdoDataset(dsLIst.Items[ ix ]);
if ds.tag <= 0 then
FreeAndNil(dsList.Items[ix]);
end;
end;
procedure TDMShared.DataModuleDestroy(Sender: TObject);
var
ix : integer;
begin
for ix := 0 to dsList.count-1 do
begin
if dsLIst.Items[ix] <> nil then
FreeAndNil(dsLIst.Items[ix]);
end;
dsList.free;
end;
好的,一个完全不同的解决方案......应该适用于 Delphi 3。
从现有数据集中创建一个新的“后代对象”到一个新单元中,并在新对象中添加一些行为。不幸的是,我没有可供测试的 Delphi 3,但如果你能找到合适的接入点,它应该可以工作。例如:
TMySharedDataset = class(tOriginalDataset)
private
fOpenCount : integer;
protected
procedure Internal_Open; override;
procedure Internal_Close; override;
end;
TMySharedDataset.Internal_Open;
begin
inherited Internal_Open;
inc(fOpenCount);
end;
TMySharedDataset.Internal_Close;
begin
dec(fOpenCount);
if fOpenCount <= 0 then
Inherited Internal_Close;
end;
然后只需将单元包含在您的数据模块中,并更改对共享数据集的引用(如果您使用组件,您还必须注册这个并将其添加到调色板)。完成此操作后,您无需更改其他单元,因为数据集仍然是原始数据集的后代。使这一切工作的原因是创建您的覆盖对象。
您可以在共享数据模块上有一个通用 TDataSet,并使用 Field 参数的 DataSet 属性在 OnDataChange 上设置它
dstDataSet := Field.DataSet;
这样,当您要关闭数据集时,请关闭数据模块上的数据集,它是指向您甚至不必知道的某种表单上的正确数据集的指针