2016-02-22 4 views
3

Я пытаюсь выполнить запрос sql асинхронно. Я проверил пример кода из http://docwiki.embarcadero.com/RADStudio/XE5/en/Asynchronous_Execution_(FireDAC)Delphi FireDAC Событие TFDQuery «AfterOpen» никогда не выполняется

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

..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-until каким-то образом подделывает цель этого асинхронного выполнения.

+0

Я жду, когда запрос завершит работу и вернет результат или отменит его работу, если что-то случится (например, кто-то поднял флаг исключения) - вот почему я хотел бы использовать асинхронный '' TFDQuery'' , Я просто взял код из более крупного проекта и поместил его в '' Button1Click'', но давайте предположим, что '' Button1Click'' - это процедура, которая выполняется внутри события '' TThread.Execute''. –

+0

Если вы ожидаете завершения запроса, лучше придерживаться 'amBlocking', так как это именно то, что вам нужно здесь. 'amAsync' имеет смысл только тогда, когда вам не нужен набор результатов непосредственно после вызова' open'. Интересно, что ваш код, вероятно, будет работать так, как ожидалось, когда вы действительно разместили его внутри метода «TThread.Execute», потому что в этом случае вы не блокируете очередь сообщений. Однако у вас будут другие проблемы с синхронизацией доступа к переменной. –

+0

Но он все еще не работает - событие '' Query1AfterOpen' еще не достигнуто. –

Смежные вопросы