Я пытаюсь выполнить запрос 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
?
Я жду, когда запрос завершит работу и вернет результат или отменит его работу, если что-то случится (например, кто-то поднял флаг исключения) - вот почему я хотел бы использовать асинхронный '' TFDQuery'' , Я просто взял код из более крупного проекта и поместил его в '' Button1Click'', но давайте предположим, что '' Button1Click'' - это процедура, которая выполняется внутри события '' TThread.Execute''. –
Если вы ожидаете завершения запроса, лучше придерживаться 'amBlocking', так как это именно то, что вам нужно здесь. 'amAsync' имеет смысл только тогда, когда вам не нужен набор результатов непосредственно после вызова' open'. Интересно, что ваш код, вероятно, будет работать так, как ожидалось, когда вы действительно разместили его внутри метода «TThread.Execute», потому что в этом случае вы не блокируете очередь сообщений. Однако у вас будут другие проблемы с синхронизацией доступа к переменной. –
Но он все еще не работает - событие '' Query1AfterOpen' еще не достигнуто. –