在我的应用程序中,我有不同的表单使用相同的数据源(因此查询也是相同的),在公共数据模块中定义。问题是,有没有办法知道我打开一个特定的查询多少次?通过能够做到这一点,我可以避免关闭该查询而不关闭它“其他地方”。
编辑:重要的是要提到我使用的是Delphi3,它不是单个查询而是几个。
答案 0 :(得分:5)
我们的想法是使用TDataSource的 DataLinks 属性 但是,由于它受到保护,您必须获得访问权限。一个常见的技巧是创建一个假的后代只是为了铸造:
type
TDataSourceHack = class(TDataSource);
然后你就像使用它一样:
IsUsed := TDataSourceHack(DataSource1).DataLinks.Count > 0;
答案 1 :(得分:3)
您可以使用类似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适用于此。您需要对TDataset后代进行适当的更改,如果您使用的是旧版本的Delphi,还需要创建一个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;
答案 2 :(得分:1)
好的,一个完全不同的解决方案......应该适用于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;
然后只需在数据模块中包含该单元,并更改对共享数据集的引用(如果使用的是组件,则还必须注册此项并将其添加到调色板中)。完成此操作后,您不必对其他单元进行更改,因为数据集仍然是原始单元的后代。使这一切工作的原因是创建了被覆盖的对象。
答案 3 :(得分:0)
您可以在共享数据模块上使用通用TDataSet,并使用Field参数的DataSet属性将其设置在OnDataChange上
dstDataSet:= Field.DataSet;
这样,当你想要关闭数据集时,关闭数据模块上的数据集,这是一个指向你甚至不需要知道的某种形式的正确数据集的指针