gpt4 book ai didi

delphi - FDManager.DeleteConnectionDef 不删除连接定义

转载 作者:行者123 更新时间:2023-12-03 02:10:38 24 4
gpt4 key购买 nike

我的应用有一个设计时 TFDConnection,当它连接到另一个数据库(类型)时,它会被重用。
我还从其设置中派生出一个池连接,并将其注册到 FDManager.AddConnectionDef 以便在多线程处理 ( like here ) 时使用。

第二次设置时,我意外地使用相同的 ConnectionDefName 再次调用了 AddConnectionDefdocumentation说:

The name must be unique across other connection definitions in the ConnectionDefs list, otherwise an exception is raised.

这不会发生。没有引发异常,我只是最终得到两个同名的 ConnectionDef。
对于那些好奇的人:下一个代码块演示了这种行为( RSP-19107 on Quality Portal )。这不是我立即面临的问题,因为我想好吧,然后我使用 DeleteConnectionDef 首先删除旧的
但事实证明这也行不通。请参阅第二个代码块。

procedure TFrmFireDACConnectionNames.BtnBug1Click(Sender: TObject);
var
lParams: TStringList;
i,l : integer;
begin
lParams := TStringList.Create;
lParams.Add('User_Name=sysdba');
lParams.Add('Password=masterkey');
lParams.Add('database=D:\Testing\test.gdb');
lParams.Add('Server=localhost');
lParams.Add('Pooled=true');
lParams.Add('DriverID=FB');
FDManager.AddConnectionDef('FBPooled','FB',lParams);
lParams.Values['database'] := 'D:\Testing\test2.gdb';
FDManager.AddConnectionDef('FBPooled','FB',lParams);

// This shows the two identical ConnectionDefs (inspect lParams):
lParams.Clear;
l := FDManager.ConnectionDefs.Count;
for i := 0 to l-1 do
lParams.Add(FDManager.ConnectionDefs[i].Name);
// Contents on my machine:
// Access_Demo
// Access_Demo_Pooled
// DBDEMOS
// EMPOYEE
// MSSQL_Demo
// RBDemos
// SQLite_Demo
// SQLite_Demo_Pooled
// FBPooled <== Duplicates
// FBPooled

// To check that the two added have their respective Params, inspect lParams with breakpoints on the lines below:
lParams.Assign(FDManager.ConnectionDefs[l-1].Params);
// Contents on my machine:
// User_Name=sysdba
// Password=masterkey
// database=D:\Testing\test2.gdb
// Server=localhost
// Pooled=true
// DriverID=FB
// Name=FBPooled

lParams.Assign(FDManager.ConnectionDefs[l-2].Params);
// Contents on my machine:
// User_Name=sysdba
// Password=masterkey
// database=D:\Testing\test.gdb
// Server=localhost
// Pooled=true
// DriverID=FB
// Name=FBPooled

lParams.Free;
end;

下面是演示 DeleteConnectionDef 失败的示例代码。请注意,我什至没有使用或打开 TFDConnection

procedure TFrmFireDACConnectionNames.BtnDeleteTestClick(Sender: TObject);
var
lParams : TStringList;
i,l : integer;
lConnName: String;
begin
lParams := TStringList.Create;
lConnName := 'MyConnPooled';

lParams.Add('DriverID=FB');
lParams.Add('User_Name=sysdba');
lParams.Add('Password=masterkey');
lParams.Add('Database=d:\Testing\Diverse\FireDACConnectionNames\test.gdb');
lParams.Add('Server=localhost');
lParams.Add('Pooled=true');

FDManager.AddConnectionDef(lConnName,'FB',lParams);

lParams.Clear;
lParams.Add('DriverID=MSSQL');
lParams.Add('User_Name=test');
lParams.Add('Password=test');
lParams.Add('Database=test');
lParams.Add('Server=VS20032008');
lParams.Add('Pooled=true');

for l := FDManager.ConnectionDefs.Count-1 downto 0 do
if FDManager.ConnectionDefs[l].Name = lConnName then
begin
FDManager.DeleteConnectionDef(lConnName); // This gets executed
Break;
end;
FDManager.AddConnectionDef(lConnName,'MSSQL',lParams);

// Check ConnectionDefs (inspect lParams):
lParams.Clear;
l := FDManager.ConnectionDefs.Count;
for i := 0 to l-1 do
lParams.Add(FDManager.ConnectionDefs[i].Name);
// Contents on my machine:
// Access_Demo
// Access_Demo_Pooled
// DBDEMOS
// EMPLOYEE
// MSSQL_Demo
// RBDemos
// SQLite_Demo
// SQLite_Demo_Pooled
// MyConnPooled <== Still duplicate
// MyConnPooled
lParams.Free;
end;

那么这里发生了什么,我该如何解决这个问题?

这是德尔福东京 10.2.1
如果您想运行此代码,请在表单上放置 TFDPhysFBDriverLinkTFDPhysMSSQLDriverLink。我尝试对这些调用 .Release,但这没有帮助。

<小时/>

更正:运行代码不需要放置 TFDPhysxxxDriverLink 组件。我保留这句话是因为它们的关联单元的存在对于 AddConnectionDefinition 错误至关重要(请参阅批准的答案)。

<小时/>

问题已解决:FireDAC.Stan.Def.pasFireDAC.Comp.Client.pas 的补丁可在 RSP-19107 获取。链接。

最佳答案

如何删除连接定义?

删除循环的问题是由于访问迭代对象而导致的,该迭代对象增加了其引用计数,这反过来又阻止了从定义集合中删除该对象。我最好避免访问该集合。

顺便说一句。这样的循环没有什么意义,因为删除方法需要名称,而不是索引,因此直接调用它基本上会产生相同的效果:

FDManager.DeleteConnectionDef(lConnName);

这样做可以避免提到的引用计数递增。但请继续阅读。

如何防止连接定义名称重复?

但要找到问题的根源。连接定义名称必须确实是唯一的,这是管理器应该注意的。不幸的是,由于您发现了错误,所以没有。在它得到修复之前,您可以简单地询问是否存在此类名称的连接定义,然后再添加一个:

if not FDManager.IsConnectionDef('FBPooled') then
FDManager.AddConnectionDef('FBPooled', 'FB', Params)
else
raise EMyException.Create('Duplicate connection definition name!');

类似的代码可以解决您所报告的问题。我会尝试描述问题所在。

防止连接定义名称重复有什么问题?

前往RSP-19107问题。嗯,它隐藏得很好。仅当应用程序中包含物理驱动程序模块时,我才能重现该问题[1]。预期的异常:

[FireDAC][Stan][Def]-255. Definition name [FBPooled] is duplicated

当应用程序中不包含物理驱动程序模块时,

会正确引发。如果包含驱动程序模块,则不会引发异常,并且具有重复名称的连接定义将添加到内部集合中。

那么,为什么当包含物理驱动程序模块时,这样的代码不会像文档声称的那样引发异常?

FDManager.AddConnectionDef('DefName', 'FB', Params);
Params.Values['Database'] := 'C:\MyDatabase.db';
FDManager.AddConnectionDef('DefName', 'FB', Params);

定义名称的重复检查位于TFDDefinition.ParamsChanged方法内,该方法反射(reflect)了对连接定义参数的更改。听起来很奇怪,但是传递给 AddConnectionDef 方法的定义名称稍后会添加到 Name 键下的定义参数中,然后引擎等待调用上述方法的更改通知ParamsChanged 方法。

AddConnectionDef 方法中的定义设置如下所示:

Definition.Params.BeginUpdate; { ← triggers TFDDefinition.ParamsChanging }
try
Definition.Params.SetStrings(Params); { ← assigns the passed parameters }
Definition.Name := 'DefName'; { ← adds (or sets) the Name key value in Params }
Definition.Params.DriverID := 'FB'; { ← creates driver specific parameter instance }
finally
Definition.Params.EndUpdate; { ← triggers TFDDefinition.ParamsChanged }
end;

第一眼看起来不错。但是 Params.DriverID 行设置有一个小问题。它触发驱动程序特定参数实例(例如TFDPhysFBConnectionDefParams)的创建,该实例替换了原始Params集合。这是正确的,但破坏了锁定。

这就是发生的情况,同样是伪代码:

Definition.Params.BeginUpdate; { ← Definition.Params.FUpdateCount += 1 }
try
Definition.Params.Free;
Definition.Params := TDriverSpecificConnectionDefParams.Create;
finally
Definition.Params.EndUpdate; { ← Definition.Params.FUpdateCount == 0 }
end;

就是这样。 Params 对象替换根本无法复制字符串列表的 FUpdateCount 值,该值需要非零才能在调用 OnChange 事件时触发 OnChange 事件>EndUpdate方法。

这就是为什么 TFDDefinition.ParamsChanged 方法没有从 finally block 触发的原因。如果您还记得我之前的段落之一,那就是定义名称重复检查所在的位置。因此,当包含驱动程序模块时,您可以添加重复项。

用伪代码解决此问题的可能方法是:

var
UpdateCount: Integer;
begin
Definition.Params.BeginUpdate; { ← Definition.Params.FUpdateCount == n }
try
UpdateCount := Definition.Params.UpdateCount; { ← store the update count }
Definition.Params.Free;
Definition.Params := TDriverSpecificConnectionDefParams.Create;
Definition.UpdateCount := UpdateCount; { ← set the update count for the new instance }
finally
Definition.Params.EndUpdate; { ← Definition.Params.FUpdateCount == n }
end;
end;
<小时/>

[1] 实际上,如果任何 FireDAC.Phys. 驱动程序文件在您的使用列表中;通过在表单上放置 TFDPhys DriverLink 组件,这些内容会自动包含在内。

关于delphi - FDManager.DeleteConnectionDef 不删除连接定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46346249/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com