Jump to content
PiedSoftware

How can you list all the datasources linked to a dataset?

Recommended Posts

Hi

 

It seems like a natural thing to want: is there a way to get all the datasouces that have dataset = ADataset? Other than looping through the components property of the owner of course. 

 

I was looking at the code of TDataset. It even has a private member variable FDatasources: TList<TDatasource>, but it is not exposed. So close!

TIA

Mark

  • Like 1

Share this post


Link to post
1 hour ago, Remy Lebeau said:

Class helpers don't have access to private members anymore since Delphi 10.1 Berlin.

 

https://blogs.embarcadero.com/closing-the-class-helpers-private-access-loophole/

Ah, yes, I've forgotten that.

 

So no other solution than RTTI or checking all components in loops.

 

Mark, you're probably not the first one in 30 years to want to access it. Perhaps you can open a "new feature" request on https://qp.embarcadero.com asking for having DataSources[] (read only) and DataSourceCount public properties on TDataSet ? (and explain why you need them)

Share this post


Link to post
15 hours ago, Patrick PREMARTIN said:

Perhaps you can try to access it from a helper on TDataSet...

I don't know if it has the right meaning though.

And since in Delphi there can only be one helper, I would avoid that approach in case someone else had a better use for it.

Edited by PiedSoftware

Share this post


Link to post

As a last resort you can define your own TDataset type with a public 'FDatasource' member and then cast the TDataset to this type. Design wise this is a clear 'hack' but it will do the job: 

 

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Datasnap.DBClient,
  Vcl.StdCtrls, System.Generics.Collections;

type
  TForm1 = class(TForm)
    ClientDataSet1: TClientDataSet;
    Button1: TButton;
    DataSource1: TDataSource;
    DataSource2: TDataSource;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  THackedDataSet = class(TComponent)
  public
    FFields: TFields;
    FAggFields: TFields;
    FFieldDefs: TFieldDefs;
    FFieldDefList: TFieldDefList;
    FFieldList: TFieldList;
    FDataSources: TList<TDataSource>;
  end;

  PHackedDataSet = ^THackedDataSet;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  var ds := PHackedDataSet(@ClientDataSet1)^;

  ShowMessage(ds.Name);

  for var src in ds.FDataSources do
    ShowMessage(src.Name);
end;

end.

 

Share this post


Link to post
10 hours ago, Keesver said:

As a last resort you can define your own TDataset type with a public 'FDatasource' member and then cast the TDataset to this type. Design wise this is a clear 'hack' but it will do the job

That approach is dependent on the layout of the original class, and could break between updates, so use with caution.

 

It is also not a last resort, either.  A safer approach would be to use RTTI to get the offset of the desired class member, and then use pointer arithmetic to access the member, eg:

uses
  ..., System.Rtti;

private
  DataSourcesOffset: Integer;

...
 
procedure TForm1.Form1Create(Sender: TObject);
var
  Ctx: TRttiContext;
begin
  DataSourcesOffset := Ctx.GetType(TClientDataSet).GetField('FDataSources').Offset;
end;

procedure TForm1.Button1Click(Sender: TObject);
type
  PDataSourceList = ^TList<TDataSource>;
begin
  var ds := PDataSourceList(PByte(ClientDataSet1) + DataSourcesOffset)^;
  for var src in ds do
    ShowMessage(src.Name);
end;

 

Edited by Remy Lebeau

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×