Delphi FDManager.DeleteConnectionDef不删除连接定义

Delphi FDManager.DeleteConnectionDef不删除连接定义,delphi,database-connection,firedac,delphi-10.2-tokyo,Delphi,Database Connection,Firedac,Delphi 10.2 Tokyo,我的应用程序有一个designtime TFDConnection,当它连接到另一个数据库类型时会被重用。 我还从池连接的设置派生一个池连接,并将其注册到FDManager.AddConnectionDef,以便在多线程处理时使用 第二次设置时,我无意中再次使用相同的ConnectionDefName调用AddConnectionDef。报告说: 该名称在ConnectionDefs列表中的其他连接定义中必须是唯一的,否则会引发异常 这种情况不会发生。没有引发任何异常,我只是得到了两个同名的co

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

第二次设置时,我无意中再次使用相同的ConnectionDefName调用AddConnectionDef。报告说:

该名称在ConnectionDefs列表中的其他连接定义中必须是唯一的,否则会引发异常

这种情况不会发生。没有引发任何异常,我只是得到了两个同名的connectiondf。 对于那些好奇的人:下一个代码块演示了这种行为。这不是我马上要解决的问题,因为我想得很好,然后我首先使用deleteConnectionDF删除旧的。 但事实证明,这也不起作用。请参阅第二段代码

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;
那么这里会发生什么,我该如何解决

这是Delphi东京10.2.1 如果要运行此代码,请在表单上放置TFDPHYSFBRIVERLINK和TFDPHYSMSSQLDIVERLINK。我试着打电话。释放那些,但没有帮助

更正:运行代码不需要放置TFDPHYXXXDRIVERLINK组件。我之所以保留这句话,是因为它们的关联单元的存在对于AddConnectionDefinition bug是必不可少的,请参见批准的答案

问题已解决:FireDAC.Stan.Def.pas和FireDAC.Comp.Client.pas的修补程序可从该链接获得。

如何删除连接定义? 删除循环的问题是由于访问迭代对象导致引用计数增加,从而阻止从定义集合中删除该对象。一般来说,我最好避免访问该收藏

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

FDManager.DeleteConnectionDef(lConnName);
这样做可以避免所提到的引用计数增加。但是继续读下去

如何防止连接定义名称重复? 但要从根本上解决你的问题。连接定义名称必须是唯一的,这是管理器应该注意的。不幸的是,你没有,因为你发现了一个bug。在修复之前,您只需在添加之前询问是否存在该名称的连接定义:

if not FDManager.IsConnectionDef('FBPooled') then
  FDManager.AddConnectionDef('FBPooled', 'FB', Params)
else
  raise EMyException.Create('Duplicate connection definition name!');
这样的代码可以作为您报告的问题的解决方法。我将试着描述出问题所在

防止连接定义名称重复有什么错? 对这个问题。嗯,这是一个很好的隐藏。只有当应用程序中包含物理驱动程序模块时,我才能重现该问题[1]。预期的例外情况:

[FireDAC][Stan][Def]-255。定义名称[FBPooled]重复

当应用程序中不包括物理驱动器模块时,正确升起。如果包含驱动程序模块,则不会引发异常,并将具有重复名称的连接定义添加到内部集合中

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

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。没错,但是锁坏了

这也是在伪代码中发生的:

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值,该值必须为非零,才能在调用EndUpdate方法时触发OnChange事件

这就是为什么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.phy。驱动程序文件在您的使用列表中;通过在表单上放置TFDPhysDriverLink组件,可以自动包含这些内容。

如何删除连接定义? 删除循环的问题是由于访问迭代对象导致引用计数增加,从而阻止从def中删除该对象 初始化集合。一般来说,我最好避免访问该收藏

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

FDManager.DeleteConnectionDef(lConnName);
这样做可以避免所提到的引用计数增加。但是继续读下去

如何防止连接定义名称重复? 但要从根本上解决你的问题。连接定义名称必须是唯一的,这是管理器应该注意的。不幸的是,你没有,因为你发现了一个bug。在修复之前,您只需在添加之前询问是否存在该名称的连接定义:

if not FDManager.IsConnectionDef('FBPooled') then
  FDManager.AddConnectionDef('FBPooled', 'FB', Params)
else
  raise EMyException.Create('Duplicate connection definition name!');
这样的代码可以作为您报告的问题的解决方法。我将试着描述出问题所在

防止连接定义名称重复有什么错? 对这个问题。嗯,这是一个很好的隐藏。只有当应用程序中包含物理驱动程序模块时,我才能重现该问题[1]。预期的例外情况:

[FireDAC][Stan][Def]-255。定义名称[FBPooled]重复

当应用程序中不包括物理驱动器模块时,正确升起。如果包含驱动程序模块,则不会引发异常,并将具有重复名称的连接定义添加到内部集合中

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

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。没错,但是锁坏了

这也是在伪代码中发生的:

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值,该值必须为非零,才能在调用EndUpdate方法时触发OnChange事件

这就是为什么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.phy。驱动程序文件在您的使用列表中;通过在表单上放置TFDPhysDriverLink组件,可以自动包含这些内容。

这是一些检测工作!当AddConnectionDef进入InterfaceIdStandDefinitions时,我已停止跟踪它;-我错过了FireDAC新出现的IsConnectionDef的存在。顺便说一句,我在你的答案中编辑了一个脚注。@Jan,我花了一段时间才找到它。我怀疑接口引用,在搜索不调用ParamsChanged方法的原因时,完全忽略了在其自身更新锁中的对象替换。谢谢你的脚注;我只是把它格式化,使之与文章的其余部分相适应。你的第一段话太真实了!!应用IsConnectionDef“fix”后,我的测试程序运行良好,但我的实际程序在重复连接定义名称方面一直存在问题。原因:一个简单的事实是我正在阅读connectiondfs[]。查看是否一切正常!真是浪费时间。我现在在RSP-19107中加上这一点,我认为它太详细了,不能这么说。@Victoria:又是一个一流的答案+我很高兴有人能找出FDac的复杂之处。那是一些侦探工作!当AddConnectionDef进入InterfaceIdStandDefinitions时,我已停止跟踪它;-我错过了FireDAC新出现的IsConnectionDef的存在。顺便说一句,我在你的答案中编辑了一个脚注。@Jan,我花了一段时间才找到它。我怀疑接口引用,在搜索不调用ParamsChanged方法的原因时,完全忽略了在其自身更新锁中的对象替换。谢谢你的脚注;我只是把它格式化,使之与文章的其余部分相适应。你的第一段话太真实了!!应用IsConnectionDef“fix”后,我的测试程序运行良好,但我的实际程序在重复连接定义名称方面一直存在问题。原因:一个简单的事实是我正在阅读connectiondfs[]。查看是否一切正常!真是浪费时间。我现在在RSP-19107中加上这一点,我认为它太详细了,不能这么说。@Victoria:又是一个一流的答案+1我 我很高兴有人能理解FDac的复杂性。