Maintain an array of same object in Delphi

I want to maintain an array of TADOConnection in Delphi.
I have a question related to this, but I think the fact it is TADOConnection instead of another component is irrelevant.
My application is divided in modules (each has its form)
The module creates its own TADOConnection (.Create method) but calls one single function, says FConnect to connect. The function FConnect remembers this connection by setting its .Tag to a integer value, and remembers it by putting it in a global array, says G:array[1..x] of TADOConnection, like this : G[N] := PassedADOConnectionObject;
When the entire Application terminates, it calls another function says FDisconnect that should nicely close all the connections that were remembered.
My question is this : some modules have already freed (Free method) "their" connection.
How can the centralised FDisconnect "detect" it has not to close some because they have already been freed ?

LVL 1
LeTayAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Russell LibbySoftware Engineer, Advisory Commented:
It really comes down to registering for, and handling the free notification from the component (in this case, the TADOConnection). Provided below is a sample container class that handles this for you, and can be extended to handle your other base methods.

Regards,
Russell

----

unit Unit2;

interface

uses
  Windows, SysUtils, Classes, ADODB;

type
  // Connection manager
  TADOConnections   =  class(TComponent)
  private
     // Private declarations
     FConnections:  TList;
  protected
     // Protected declarations
     procedure      Notification(AComponent: TComponent; Operation: TOperation); override;
     function       GetItems(Index: Integer): TADOConnection;
     function       GetCount: Integer;
  public
     // Public declarations
     constructor    Create(AOwner: TComponent); override;
     destructor     Destroy; override;
     function       Add(Item: TADOConnection): Integer;
     procedure      Delete(Index: Integer);
     procedure      DisconnectAll;
     procedure      Remove(Item: TADOConnection);
     property       Count: Integer read GetCount;
     property       Items[Index: Integer]: TADOConnection read GetItems;
  end;

implementation

procedure TADOConnections.Notification(AComponent: TComponent; Operation: TOperation);
begin

  // Check for free
  if (Operation = opRemove) and Assigned(AComponent) then
  begin
     // Item is being freed, remove it from the list
     FConnections.Remove(AComponent);
  end;

end;

function TADOConnections.GetItems(Index: Integer): TADOConnection;
begin

  // Return item
  result:=TADOConnection(FConnections[Index]);

end;

function TADOConnections.GetCount: Integer;
begin

  // Return count of items
  result:=FConnections.Count;

end;

function TADOConnections.Add(Item: TADOConnection): Integer;
begin

  // Default result
  result:=(-1);

  // Check item
  if Assigned(Item) then
  begin
     // We want free notification on this component
     Item.FreeNotification(Self);
     // Resource protection
     try
        // Add to list
        result:=FConnections.Add(Item);
     finally
        // Set item tag
        Item.Tag:=result;
     end;
  end;

end;

procedure TADOConnections.Delete(Index: Integer);
begin

  // Remove item from the list, but do not free it
  FConnections.Delete(Index);

end;

procedure TADOConnections.Remove(Item: TADOConnection);
begin

  // Remove item from the list, but do not free it
  FConnections.Remove(Item);

end;

procedure TADOConnections.DisconnectAll;
var  adcItem:       TADOConnection;
     dwIndex:       Integer;
begin

  // Walk all items
  for dwIndex:=Pred(FConnections.Count) downto 0 do
  begin
     // Get connection
     adcItem:=TADOConnection(FConnections[dwIndex]);
     // Resource protection
     try
        // Disconnect
        adcItem.Connected:=False;
        // Remove free notification (we don't want notification)
        adcItem.RemoveFreeNotification(Self);
        // Free the item
        adcItem.Free;
     finally
        // Remove from the list
        FConnections.Delete(dwIndex);
     end;
  end;

end;

constructor TADOConnections.Create(AOwner: TComponent);
begin

  // Perform inherited
  inherited Create(AOwner);

  // Create connection list
  FConnections:=TList.Create;

end;

destructor TADOConnections.Destroy;
begin

  // Resource protection
  try
     // Disconnect all
     DisconnectAll;
     // Free list
     FConnections.Free;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

end.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ziolkoCommented:
uses contnrs

there's very nice class called TObjectList, you can ovverride Notify() method like this:
  if OwnsObjects then
    if Action = lnDeleted then begin
      TADOConnection(Ptr).Close;
      TObject(Ptr).Free;
    end;
  inherited Notify(Ptr, Action);

this way any time you remove ADOConnection object from list it will disconnect before destroying object, that also includes .Clear method and list's destructor



zioklo.
0
Russell LibbySoftware Engineer, Advisory Commented:

It should be noted that TObject list will NOT give you notifcation if the component is freed independently from the list (eg, the modules free the connection directly), as the only way to get notification of this is by ensuring that FreeNofication is setup.

Russell

0
Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

LeTayAuthor Commented:
Dear rllibby,
I will start using your code to manage these connections
But in the mean time, I have a last question, in the frame of managing it with a TList component.
What is going to happen if I "try" to use some method and properties of a TADOConnection referenced in the TList, when at another place in the application, that TADOConnection component has already been freed ?
Of course at the end, I will not have to worry about this because I will cleanup all my code, by I have more than 50.000 lines of code and I want a smooth "migration"
Do I just have to put this code in a try except block and "understand" that the object is already gone if an exception occur ?
0
Russell LibbySoftware Engineer, Advisory Commented:

The TList is only used as a container for the pointers (in this case, pointers to components), it does nothing to manage the lifetime. The lifetime management is done from the component handler (TADOConnections). When you add a connection to the list, the component collection requests that it be notified when the object is freed. eg:

function TADOConnections.Add(Item: TADOConnection): Integer;
begin

  // Default result
  result:=(-1);

  // Check item
  if Assigned(Item) then
  begin
     // We want free notification on this component
     Item.FreeNotification(Self);
     // Resource protection
     try
        // Add to list
        result:=FConnections.Add(Item);
     finally
        // Set item tag
        Item.Tag:=result;
     end;
  end;

end;

If the component is freed, then a notification event is sent to the collection manager informing of this:

procedure TADOConnections.Notification(AComponent: TComponent; Operation: TOperation);
begin

  // Check for free
  if (Operation = opRemove) and Assigned(AComponent) then
  begin
     // Item is being freed, remove it from the list
     FConnections.Remove(AComponent);
  end;

end;

At this point, the collection manager removes the item from the list, ensuring that all items in the list are valid and have not yet been freed.

Russell

0
LeTayAuthor Commented:
I understand that and will implement it
But I just want to know what happens in the situation I describe, which will be temporary, as later on, I will use your methods 100 %
So what happens if try to Free something that is already freed ?
A traditional exception ?
0
developmentguruPresidentCommented:
 I think the whole situation could be simplified by looking into some "Best Practices" for Delphi.  As I understand it, the TADOConnection should only be created once unless you need to multi thread the database access (or access multiple databases, then one for each).  All of your forms can use the same TADOConnection by simply placing one on a data module.  You will need to be sure that the data module is created before any forms that will want to make use of this shared connection component.  On each form that needs a TADOQuery or TADOTable you can place the component on the form and point it's connection to the DataModule1.ADOConnection1 (you will need to add the data module to the uses clause).  This has the possibility of removing a lot of code from your project.  With the components either on the data module or on the form you will neither need to create or destroy them (it's handled for you).  If you still need to dynamically create datasets you can still simply point them to the data module connection property.

  I have a project that needs to be able to connect either to a local database or to a remote database based on several criteria.  I have created a data module that exports two methods 1) GetQuery and 2) ReleaseQuery.  GetQuery creates a TADOQuery and sets it up to use the correct connection and passes it back to me.  I follow a call to that function with a try / finally block and call ReleaseQuery (giving me a chance to do any common processing I want there).  Perhaps your individual form's code for creating the connection and datasets could be done this way (simplifying creating new forms while standardizing the code).

Just a thought...
0
Russell LibbySoftware Engineer, Advisory Commented:
>> I understand that and will implement it
But I just want to know what happens in the situation I describe, which will be temporary, as later on, I will use your methods 100 %
So what happens if try to Free something that is already freed ?
A traditional exception ?

If you attempt to use something that has been freed, then the results will be undefined; an exception might be raised, or raised further down the line in unrelated code. It all depends on IF the memory the component occupied has been overwritten by something else, etc. Its best to not get yourself in that sort of situation.

And good points have been made in regards to best practices, something you may want to consider. I have not inquired as to WHY you need mutliple connections, I am only demonstrating one way to do it safely.

Russell



0
Computer101Commented:
Forced accept.

Computer101
EE Admin
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.