FDManager.DeleteConnectionDef不删除连接定义
FDManager.DeleteConnectionDef does not delete connection definition
我的应用程序有一个设计时 TFDConnection
,当它连接到另一个数据库(类型)时会被重用。
我还从其设置中派生了一个池连接,并将其注册到 FDManager.AddConnectionDef
以在多线程时使用 (like here).
第二次设置时,我不小心用相同的 ConnectionDefName 再次调用了 AddConnectionDef
。 documentation 表示:
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
先删除旧的 。
但是结果that也不行。见第二段代码。
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;
这是怎么回事,我该如何解决?
这是Delphi东京10.2.1
如果您想 运行 此代码,请在您的表单上放置 TFDPhysFBDriverLink
和 TFDPhysMSSQLDriverLink
。我尝试在这些上调用 .Release,但这没有帮助。
更正:运行编译代码不需要放置 TFDPhysxxxDriverLink 组件。我将这句话留在原处,因为它们的关联单元的 存在 对于 AddConnectionDefinition 错误至关重要(请参阅已批准的答案)。
问题已解决:FireDAC.Stan.Def.pas
和 FireDAC.Comp.Client.pas
的补丁在 RSP-19107 link.
可用
如何删除连接定义?
删除循环的问题是由访问迭代对象引起的,该对象增加了它们的引用计数,这反过来又阻止了从定义集合中删除该对象。一般情况下,我最好避免访问该集合。
顺便说一句。这样的循环没有什么意义,因为删除方法需要的是名称,而不是索引,所以直接调用它的效果基本相同:
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 方法中对定义名称进行了重复检查,这反映了对连接定义参数的更改。听起来很奇怪,但是传递给 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 调用 EndUpdate 方法时的事件。
这就是为什么 TFDDefinition.ParamsChanged 方法没有从 finally 块中触发的原因。如果您还记得我之前的一段话,那就是对定义名称进行重复检查的地方。因此,您可以在包含驱动程序模块时添加重复项。
伪代码中可能解决此问题的方法是:
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. 驱动程序文件在您的使用列表中;通过在表单上放置 TFDPhysDriverLink 组件可以自动包含这些。
我的应用程序有一个设计时 TFDConnection
,当它连接到另一个数据库(类型)时会被重用。
我还从其设置中派生了一个池连接,并将其注册到 FDManager.AddConnectionDef
以在多线程时使用 (like here).
第二次设置时,我不小心用相同的 ConnectionDefName 再次调用了 AddConnectionDef
。 documentation 表示:
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
先删除旧的 。
但是结果that也不行。见第二段代码。
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;
这是怎么回事,我该如何解决?
这是Delphi东京10.2.1
如果您想 运行 此代码,请在您的表单上放置 TFDPhysFBDriverLink
和 TFDPhysMSSQLDriverLink
。我尝试在这些上调用 .Release,但这没有帮助。
更正:运行编译代码不需要放置 TFDPhysxxxDriverLink 组件。我将这句话留在原处,因为它们的关联单元的 存在 对于 AddConnectionDefinition 错误至关重要(请参阅已批准的答案)。
问题已解决:FireDAC.Stan.Def.pas
和 FireDAC.Comp.Client.pas
的补丁在 RSP-19107 link.
如何删除连接定义?
删除循环的问题是由访问迭代对象引起的,该对象增加了它们的引用计数,这反过来又阻止了从定义集合中删除该对象。一般情况下,我最好避免访问该集合。
顺便说一句。这样的循环没有什么意义,因为删除方法需要的是名称,而不是索引,所以直接调用它的效果基本相同:
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 方法中对定义名称进行了重复检查,这反映了对连接定义参数的更改。听起来很奇怪,但是传递给 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 调用 EndUpdate 方法时的事件。
这就是为什么 TFDDefinition.ParamsChanged 方法没有从 finally 块中触发的原因。如果您还记得我之前的一段话,那就是对定义名称进行重复检查的地方。因此,您可以在包含驱动程序模块时添加重复项。
伪代码中可能解决此问题的方法是:
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.