Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Delphi TADOQuery asynchron abfragen (https://www.delphipraxis.net/142932-tadoquery-asynchron-abfragen.html)

Norbert5 6. Nov 2009 08:29

Datenbank: MS SQL Server 2005 • Zugriff über: ADO

TADOQuery asynchron abfragen
 
Ich verbinde eine TADOQuery mit einem DBGrid.
Damit der Benutzer nicht auf das komplette SQL warten muss, arbeite ich asynchron.

Das Grid zeigt aber grundsätzlich den 2. Datensatz als ersten an.
Erst nach einem Next/Prior/Prior komme ich zum 1. Datensatz.
Ebenso positioniert ein First wieder auf Datensatz 2.
Offensichtlich ist BOF true beim 2. Datensatz!

Synchron sind Query und Grid okay.

Hat jemand eine Idee?

Hier mein Code:
[delphi]
type
TForm3 = class(TForm)
PF32DB: TADOConnection;
DataSource1: TDataSource;
OVCDBTable1: TDBGrid;
DBNavigator1: TDBNavigator;
Button1: TButton;
ADOTable1: TADOQuery;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private-Deklarationen }
isexecuting: boolean;
iscanceling: boolean;
aktreccnt: integer;
iworkasync: boolean;
procedure OpenTable;
procedure _onFetchProgress(DataSet: TCustomADODataSet;
Progress, MaxProgress: Integer; var EventStatus: TEventStatus);
procedure _onFetchComplete(DataSet: TCustomADODataSet;
const Error: Error; var EventStatus: TEventStatus);
public
procedure WaitForFirstData;
procedure WaitForAllData;
property Executing: boolean read isexecuting;
public
{ Public-Deklarationen }
end;

var
Form3: TForm3;

const
workasync = true;

implementation

{$R *.dfm}

procedure TForm3.Button1Click(Sender: TObject);
begin
ADOTable1.first;
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
PF32DB.Connected := true;
with ADOTable1 do
begin
SQL.Clear;
SQL.Add('Select * from PFPK where tbljahr=2009');
OpenTable;
end;
end;

procedure TForm3.OpenTable;
begin
iworkasync := workasync;
isexecuting := false;
aktreccnt := 0;
with ADOTable1 do
begin
if active then //already opened/closed?
exit;
if not assigned(Connection) then
iworkasync := false;
if (csDesigning in ComponentState) then //no async in Designer
iworkasync := false;
if not iworkasync then
begin
ExecuteOptions := [];
active := true;
exit;
end;
DisableControls;
ExecuteOptions := ExecuteOptions + [eoAsyncExecute, eoAsyncFetch{, eoAsyncFetchNonblocking}];
isexecuting := true;
iscanceling := false;
OnFetchProgress := _OnFetchProgress;
OnFetchComplete := _OnFetchComplete;
aktreccnt := 0;
active := true;
while (isexecuting and (aktreccnt=0))do //wait for first datablock
application.ProcessMessages;
EnableControls;
end;
end;

procedure TForm3.WaitForFirstData;
begin
if (csDesigning in ComponentState) then exit;
while (isexecuting and (aktreccnt=0))do
application.ProcessMessages;
end;

procedure TForm3.WaitForAllData;
begin
if (csDesigning in ComponentState) then exit;
while isexecuting do
application.ProcessMessages;
end;

procedure TForm3._onFetchProgress(DataSet: TCustomADODataSet;
Progress, MaxProgress: Integer; var EventStatus: TEventStatus);
begin
if Progress=MaxProgress then
begin
isexecuting := false;
exit;
end;
if (iscanceling and assigned(Dataset) and assigned(Dataset.Recordset)) then
begin
Dataset.Recordset.Cancel;
end;
aktreccnt := Progress;
end;

procedure TForm3._onFetchComplete(DataSet: TCustomADODataSet;
const Error: Error; var EventStatus: TEventStatus);
begin
isexecuting := false;
iscanceling := false;
end;

end.

Norbert5 9. Nov 2009 23:11

Re: TADOQuery asynchron abfragen
 
:wall: Nach einigen Stunden im Debugger (bei asynchronen Routinen macht das Debuggen doppelt Spass) bin ich ein wenig schlauer.
Aber geholfen hat's noch nicht.

TDataSet.First ruft zuerst InternalFirst auf,
bei ADO bedeutet dies MoveFirst + MovePrevious
=> recordset-objekt wird vor den ersten Satz gesetzt; recordset.bof ist true

Danach wird GetNextRecord aufgerufen,
resultiert in recordset.MoveNext.

so weit, so logisch

Aber:

steht der ADO-Recordset auf BOF, positioniert recordset.MoveNext

- bei synchronem Execute auf den 1. Datensatz :-D
- bei asynchronem Execute auf den 2. Datensatz :gruebel:

Muss ich mich jetzt bei Microsoft beschweren?

Bernhard Geyer 10. Nov 2009 07:34

Re: TADOQuery asynchron abfragen
 
Welche Delphi-Version hast du den? Alle Updates installiert?

simmi 10. Nov 2009 07:51

Re: TADOQuery asynchron abfragen
 
Hallo,

ich hatte das gleiche Problem mit ADO. Es wurde sogar noch schlimmer, weil irgendwann die ganzen Datensätze durcheinander waren.

Als brauchbare Lösung erwies sich, statt mit ADO mit SQL Direkt zu arbeiten. Das funktioniert schnell und bisher Problemlos.

Norbert5 10. Nov 2009 09:11

Re: TADOQuery asynchron abfragen
 
Erstmal Dank für eure Antworten.

Zu 1: Probiert (und den Fehler reproduziert) habe ich mit Delphi-7 und RAD-Studio 2007.

Zu 2: Mit TSQLQuery kann ich keine asynchrone Bearbeitung machen
oder habe ich da was übersehen :?:

Norbert5 10. Nov 2009 10:27

Re: TADOQuery asynchron abfragen
 
Noch ein Nachtrag:

Inzwischen habe ich bei Microsoft zwei verwandte Einträge in der Fehler-Datenbank gefunden:

http://kbupdate.info/microsoft-data-...ac-2-6-bug.php

Nr. 287083 (Mai 2003) - first row not stored if bound to controlelement
Nr. 245509 (März 2005) - error "Async operation is pending"

In beiden Fällen bestätigt Microsoft einen eigenen Fehler;
Zu 1 empfiehlt MS, mit einem Recordset-Clone zu arbeiten
(das würde Delphis Tabellensteuerung völlig aus der Bahn werfen)
oder Server-Cursor zu verwenden (hat bei mir das Verhalten nicht geändert)
Zu 2 rät MS zur Umstellung auf synchrone Bearbeitung - sehr sinnig.

:angel: Meine Begeisterung wächst stündlich ...

Norbert5 10. Nov 2009 22:26

Re: TADOQuery asynchron abfragen
 
:cheers: Heureka, ich hab's gefunden.

Den Fehler von Microsoft kann ich nicht korrigieren,
aber eine kleine Änderung in ADODB umgeht diesen Fehler.

Routine TCustomADODataSet.InternalGetRecord
(RAD Studio 2007: Zeile 4179)

bisher:
Delphi-Quellcode:
           
             if not Recordset.EOF then
               Recordset.MoveNext;
neu:
Delphi-Quellcode:
           
              if not Recordset.EOF then
              begin
                if Recordset.BOF
                  then Recordset.MoveFirst
                  else Recordset.MoveNext;
              end;
Vielen Dank für Eure Bemühungen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:30 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz