FDManager.DeleteConnectionDef不会删除连接定义(FDManager.DeleteConnectionDef does not delete connection defin

编程入门 行业动态 更新时间:2024-10-17 17:27:46
FDManager.DeleteConnectionDef不会删除连接定义(FDManager.DeleteConnectionDef does not delete connection definition)

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

第二次设置时,我意外地使用相同的ConnectionDefName再次调用AddConnectionDef 。 文件说:

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

这不会发生 。 没有异常,我最终得到两个具有相同名称的ConnectionDefs。 对于那些好奇的人:下一个代码块演示了这种行为( 质量门户网站上的RSP-19107 )。 这不是我的问题,因为我认为好,然后我使用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;

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

这是Delphi Tokyo 10.2.1 如果要运行此代码, TFDPhysMSSQLDriverLink在表单上放置TFDPhysFBDriverLink和TFDPhysMSSQLDriverLink 。 我试着打电话。发布那些,但这没有用。


更正:运行代码不需要放置TFDPhysxxxDriverLink组件。 我将离开句子,因为其关联单元的存在对于AddConnectionDefinition错误至关重要(请参阅已批准的答案)。


已解决的问题:可在该RSP-19107链接上获取FireDAC.Stan.Def.pas和FireDAC.Comp.Client.pas修补程序。

My app has a designtime TFDConnection that gets reused when it connects to another database (type). I also derive a pooled connection from its settings, and register this with FDManager.AddConnectionDef to be used when multithreading (like here).

When setting this up a second time I accidentally called AddConnectionDef again with the same ConnectionDefName. The documentation says:

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

This does not happen. No exception is raised, I just end up with two ConnectionDefs with the same name. For those curious: the next code block demonstrates this behaviour (RSP-19107 on Quality Portal). This is not my immediately issue, because I thought Well, then I use DeleteConnectionDef to remove the old one first. But it turns out that that does not work either. See the second block of code.

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;

Below is the sample code demonstrating the DeleteConnectionDef failing. Note that I do not even use or open a 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;

So what can be going on here, and how can I fix this?

This is Delphi Tokyo 10.2.1 If you want to run this code, place a TFDPhysFBDriverLink and TFDPhysMSSQLDriverLink on your form. I tried calling .Release on those, but that did not help.


Correction: Placing the TFDPhysxxxDriverLink components is not necessary for running the code. I'm leaving the sentence in because the presence of their associated units is essential for the AddConnectionDefinition bug (see approved answer).


Issue resolved: Patches for FireDAC.Stan.Def.pas and FireDAC.Comp.Client.pas are available at that RSP-19107 link.

最满意答案

如何删除连接定义?

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

顺便说一句。 这样的循环没有多大意义,因为删除方法需要name而不是index,所以直接调用它会产生基本相同的效果:

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] [斯坦] [定义] -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 ),它取代了原始的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值,在调用EndUpdate方法时,该值必须为非零才能触发OnChange事件。

这就是为什么TFDDefinition.ParamsChanged方法不是从最后一个块触发的原因。 如果你还记得我以前的一个段落,那就是重复检查定义名称的地方。 因此,您可以在包含驱动程序模块时添加重复项。

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

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。<DBMS>驱动程序文件在您的使用列表中; 这些是通过在表单上放置TFDPhys <DBMS> DriverLink组件自动包含的。

How to delete connection definition?

Problem with your deletion loop is caused by accessing the iterated object which incremented their reference count, which in turn prevented that object removal from the definition collection. I'd better avoid accessing that collection in general.

Btw. such loop makes little sense because the deletion method expects name, not index, so calling it directly will have essentially the same effect:

FDManager.DeleteConnectionDef(lConnName);

By doing so you avoid the mentioned reference count incrementing. But keep on reading.

How to prevent connection definition name duplicates?

But to the root of your problem. Connection definition names must really be unique, and that's what the manager should take care of. Unfortunately doesn't, because of the bug you've found. Before it gets fixed, you can simply ask if there is a connection definition of such name before you add one:

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

Code like that can be a workaround for the issue you've reported. I'll try to describe what's wrong.

What's wrong with preventing connection definition name duplicates?

To the RSP-19107 issue. Well, it's very well hidden one. I was able to reproduce the issue only if a physical driver module was included in the application[1]. The expected exception:

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

is correctly raised when no physical driver module is included in the application. If there is a driver module included, no exception is raised, and connection definition with a duplicate name is added to the internal collection.

So, why code like this doesn't raise exception as the documentation claims when a physical driver module is included?

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

A duplicate check for definition name is inside the TFDDefinition.ParamsChanged method which reflects changes to the connection definition parameters. Sounds weird, but definition name that is passed to the AddConnectionDef method is later added to the definition parameters under Name key, and the engine then waits for change notification which calls the mentioned ParamsChanged method.

Definition setup in the AddConnectionDef method reads like this:

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;

It looks fine on the first view. But there's one little problem with the line setting Params.DriverID. It triggers creation of driver specific parameters instance (e.g. TFDPhysFBConnectionDefParams) which replaces the original Params collection. That's correct, but breaks the lock.

That's what happens, again in pseudocode:

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

That's it. The Params object replacement simply cannot copy the string list's FUpdateCount value, which needs to be non-zero to trigger the OnChange event when calling EndUpdate method.

So that's the reason why TFDDefinition.ParamsChanged method is not triggered from that finally block. And if you remember one of my previous paragraphs, that is the place where duplicate check for definition name resides. Hence you are able to add duplicates when a driver module is included.

A possible fix of this issue in pseudocode would be:

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] Actually, if any of the FireDAC.Phys.<DBMS> driver files is in your uses list; these are included automatically by placing a TFDPhys<DBMS>DriverLink component on the form.

更多推荐

本文发布于:2023-08-01 21:40:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1365802.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:定义   DeleteConnectionDef   FDManager   definition   connection

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!