4
votes

I'm trying to execute an sql query asynchronously. I've checked the example code from http://docwiki.embarcadero.com/RADStudio/XE5/en/Asynchronous_Execution_(FireDAC)

and also the example project from directory

..Samples\Object Pascal\Database\FireDAC\Samples\Comp Layer\TFDQuery\ExecSQL\Async

and I think I got the logic inside. But there is one problem - the event QueryAfterOpen never executes and my TDataSource remains always Nil (because it gets Nil inside QueryBeforeOpen - this event executes always). This is all the code from my unit :

unit Unit1;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDAC.Stan.Intf,   
  FireDAC.Stan.Option, FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, 
  FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys,       
  FireDAC.VCLUI.Wait, FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt,
  Vcl.StdCtrls, Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client,    
  FireDAC.Phys.MySQLDef, FireDAC.Phys.MySQL;

type
  TForm1 = class(TForm)
  Button1: TButton;
  FDPhysMySQLDriverLink1: TFDPhysMySQLDriverLink;
  procedure FormCreate(Sender: TObject);
  procedure Button1Click(Sender: TObject);
  procedure Query1BeforeOpen(DataSet: TDataSet);
  procedure Query1AfterOpen(DataSet: TDataSet);
  procedure FormDestroy(Sender: TObject);

private
  { Private declarations }
  Connection1: TFDConnection;
  Query1: TFDQuery;
  DataSource1: TDataSource;
public
  { Public declarations }
end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
  var
    QueryFinished: Boolean;
begin
  with Query1 do begin
    SQL.Text := 'SELECT field1 FROM test_table WHERE idpk=1';
    AfterOpen := Query1AfterOpen;
    BeforeOpen := Query1BeforeOpen;
    ResourceOptions.CmdExecMode := amAsync;
    QueryFinished := False;
    Open;
    repeat
      Sleep(100);
      if Command.State = csPrepared then begin
          // A command is prepared. A result set is not accessible.
        // TmpInteger := Query1.FieldByName('field1').AsInteger;
      end
      else if Command.State = csOpen then begin // A command execution
         // is finished. A result set is accessible and not yet fully fetched.
        if DataSource1.DataSet <> Nil then begin 
          // this code never executes because Query1AfterOpen never executes and 
          // DataSource1.DataSet remains always Nil !!!
          QueryFinished := True;
        end;
      end;
    until ((QueryFinished) OR (DataSource1.DataSet <> Nil));
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
  var
    SQLConnParams: string;
  begin
    SQLConnParams := ''; // sql connection parameters removed from here from security 
    // issues, assume they are correct
    Connection1 := TFDConnection.Create(Nil);
    Connection1.Params.Text := SQLConnParams;
    Query1 := TFDQuery.Create(Nil);
    Query1.Connection := Connection1;
    DataSource1 := TDataSource.Create(Nil);
    DataSource1.DataSet := Query1;
  end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  DataSource1.Free;
  Query1.Free;
  Connection1.Free;
end;

procedure TForm1.Query1AfterOpen(DataSet: TDataSet);
begin
  DataSource1.DataSet := Query1;
  Query1.AfterOpen := Nil;
  //Query1.ResourceOptions.CmdExecMode := amBlocking;
end;

procedure TForm1.Query1BeforeOpen(DataSet: TDataSet);
begin
  DataSource1.DataSet := Nil;
end;
end.

Apparently the code inside the repeat .. until .. loop is infinite unless someone terminates the program. What am I missing in order to execute the code inside Query1AfterOpen (or use another event instead) so I can access the result set after the TFDQuery has finished working ?

1

1 Answers

1
votes

Those asynchronous events are synchronized with the main thread via the message loop. As long as you stay inside the Button1Click event no new messages can be handled. Thus the AfterOpen event is stuck inside the message loop.

I don't know what you are trying to achieve, but you should consider placing the relevant code in the AfterOpen event. The repeat-until clause somehow counterfeits the purpose of that async execution.