delphipersistentfiredacmigrating

Delphi: Migrating from IBO to FireDac


So, lately we (me and my coworkers) have been chatting about migrating to FireDac, we are currently using IBO and DBX, but mostly IBO. And then we decided to take everything from IBO to FireDac, but entering in every form, changing every IBOQuery, adding all fields, settings all the display format, etc, etc, etc, would take too much time, so we decided to make a component do it, seemed like an easy task, but I just started and I'm already stuck in something that seems simple, but that I never came across before. First let's look at the component code:

    unit UMyComponent;

    interface

    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IB_Components, IB_Access,
      IBODataset, Vcl.StdCtrls, Vcl.Buttons, Vcl.Grids, Vcl.DBGrids, Data.DB,
      uADStanIntf, uADStanOption, uADStanParam, uADStanError,
      uADDatSManager, uADPhysIntf, uADDAptIntf, uADStanAsync, uADDAptManager,
      uADCompDataSet, uADCompClient;

    type
      TMyComponent = class(TComponent)
      private
        FADConnection: TADConnection;
        FConverter: String;

        procedure Iniciar;

        procedure SetADConnection(const Value: TADConnection);
        procedure SetConverter(const Value: String);
      published
        property Converter: String read FConverter write SetConverter;
        property ADConnection: TADConnection read FADConnection write SetADConnection;
      end;

    procedure Register;

    implementation

    procedure Register;
    begin
      RegisterComponents('MyComponents', [TMyComponent]);
    end;

    { TMyComponent }

    procedure TMyComponent.Iniciar;
    var
      Form: TForm;
      IBOQuery: TIBOQuery;
      i: Integer;

      procedure _ConverterIBOQuery;
      var
        ADQuery: TADQuery;
        qName: String;
      begin
        qName := IBOQuery.Name;

        if qName.Contains('OLD_') then
          Exit;

        IBOQuery.Name := 'OLD_'+ qName;

        if (FindComponent(qName) = nil) then
        begin
          ADQuery            := TADQuery.Create(Form);
          ADQuery.Name       := qName;
          ADQuery.Connection := FADConnection;
          ADQuery.SQL        := IBOQuery.SQL;

          {
           I need to add the fields here, but I need them having a reference,
           like the ones you Right Click > Fields Editor > Add All Fields (CTRL + F)
           because in the final form of this component, it won't rename the old query
           with an 'OLD_' prefix, it will destroy it, and the fields will be gone too,
           so I need to add them (having the reference) in order to not rewrite any of my code
          }
        end;
      end;
    begin
      if Owner is TForm then
        Form := TForm(Owner);

      if Assigned(Form) then
      begin
        for i := 0 to (Form.ComponentCount -1) do
          {
           I know it will stop in the first query it come across,
           but I'm trying to full convert only one to know if it's actually possible
          }
          if (Form.Components[i] is TIBOQuery) then
          begin
            IBOQuery := TIBOQuery(Form.Components[i]);
            Break;
          end;

        if Assigned(IBOQuery) then
          _ConverterIBOQuery;
      end;
    end;

    procedure TMyComponent.SetConverter(const Value: String);
    begin
      FConverter := UpperCase(Value[1]);

      if (FConverter = 'S') then
        Iniciar;

      FConverter := '';
    end;

    procedure TMyComponent.SetADConnection(const Value: TADConnection);
    begin
      FADConnection := Value;
    end;

    end.

I already tried some of methods found on the internet, such as:

And none of them did what I was expecting, so I'm questioning

Can I create the field references via code? And, if it's possible, how?

And with references I mean, for example, you have IBOQuery1, and the SQL is

SELECT NAME
FROM COUNTRY

After that, you go to the Fields Editor > Add All Fields (CTRL + F), and then you have the reference IBOQuery1NAME, which is a TStringField and you can just call IBOQuery1NAME.AsString instead of IBOQuery1.FieldByName('NAME').AsString

TL;DR

Trying to create a component that migrate a IBOQuery to ADQuery, but I can't create the references


Solution

  • After many attempts and research, I found an old question with a problem similar to mine, and happily there was a answer with exactly what I wanted

    How to add a field programatically to a TAdoTable in Delphi

    The answer was provided by the user: Мסž

    procedure AddAllFields(DataSet: TDataset);
    var
       FieldsList: TStringList;
       FieldName: WideString;
       Field: TField;
       WasActive: boolean;
       FieldDef: TFieldDef;
       i: Integer;
    begin
       WasActive := DataSet.Active;
       if WasActive then
          DataSet.Active := False;
       try
          FieldsList := TStringList.Create;
          try
             DataSet.FieldDefs.Update;
    
             // make a list of all the field names that aren't already on the DataSet
             for i := 0 to DataSet.FieldDefList.Count - 1 do
                with DataSet.FieldDefList[i] do
                   if (FieldClass <> nil) and not(faHiddenCol in Attributes) then
                   begin
                      FieldName := DataSet.FieldDefList.Strings[i];
                      Field := DataSet.FindField(FieldName);
                      if (Field = nil) or (Field.Owner <> DataSet.Owner) then
                         FieldsList.Add(FieldName);
                   end;
    
             // add those fields to the dataset
             for i := 0 to FieldsList.Count - 1 do
             begin
                FieldDef := DataSet.FieldDefList.FieldByName(FieldName);
                Field := FieldDef.CreateField(DataSet.Owner, nil, FieldName, False);
                try
                   Field.name := FieldName + IntToStr(random(MaxInt)); // make the name unique
                except
                   Field.Free;
                   raise ;
                end;
             end;
          finally
             FieldsList.Free;
          end;
       finally
          if WasActive then
             DataSet.Active := true;
       end;
    end;