Событие Delphi FireDAC TFDQuery 'AfterOpen' никогда не выполняется

Я пытаюсь выполнить SQL-запрос асинхронно. Я проверил пример кода с http://docwiki.embarcadero.com/RADStudio/XE5/en/Asynchronous_Execution_(FireDAC)

а также пример проекта из каталога

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

и я думаю, что у меня есть логика внутри. Но есть одна проблема - событие QueryAfterOpen никогда не выполняет и мой TDataSource остается всегда Nil (потому что это получает Nil внутри QueryBeforeOpen - это событие выполняется всегда). Это весь код моего модуля:

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.

Видимо код внутри repeat .. until .. Цикл бесконечен, если кто-то не завершает программу. Чего мне не хватает, чтобы выполнить код внутри Query1AfterOpen (или использовать другое событие), чтобы я мог получить доступ к набору результатов после TFDQuery закончил работать?

1 ответ

Эти асинхронные события синхронизируются с основным потоком через цикл сообщений. Пока вы остаетесь внутри Button1Click событие, новые сообщения не могут быть обработаны. Таким образом, событие AfterOpen застревает внутри цикла сообщений.

Я не знаю, чего вы пытаетесь достичь, но вам следует рассмотреть возможность размещения соответствующего кода в событии AfterOpen. Предложение repeat-till каким-то образом подделывает цель этого асинхронного выполнения.

Другие вопросы по тегам