Asynchronous calls in Delphi TDARemoteDataAdapter

Hello @mh,
Now that the nextgen version of RO/DA is available for Delphi and given that asynchronous calls are mandatory in mobile environments, wouldn’t be nice to have asynchronous calls in the TDARemoteDataAdapter class?
Since Delphi XE7 we have the new threading api that simplifies parallel programming tasks.
We are available to help you to implement those methods in Delphi if you wish.

Best regards

Christen

2 Likes

Definitely something we should look at for DA9, yes. Thanx for the suggestion.

Thanks, logged as bugs://71997

You can use it as

  TTask.Create(procedure begin
                      ClientDataModule.tbl_Clients.Open;
               end).Start;

or

  TTask.Create(procedure begin
                      ClientDataModule.RDA.Fill(...);
               end).Start;

Thank you @EvgenyK,
Would be nice to be notified when the asynch call is finished.
Also, with asynch tasks the user should be able to cancel long running queries.
DOA for example includes a TOracleSession.BreakExecution that can be used for this.

this feature (canceling execution of methods on service-side) isn’t supported.
It can be handled via calling of custom service method that will call something like

   TOracleSession.BreakExecution;

Parallel Programming Library (PPL) has ITask.Status property

bugs://71997 got closed with status fixed.

Evgeny, can please explain hot to accomplish that? Is mandatory give the user a way to cancel a large query.

Best regards.

We have introduced async requests in RDA in #71997:

    function BeginFill(aCallback: TROAsyncCallback; aUserData: Pointer; aTables: array of TDADataTable; aWhereClauses : TDAWhereExpressionArray; aSaveCursor: Boolean = false; aIncludeSchema: Boolean = false; aAppendMode: Boolean = False): IROAsyncRequest;overload;
    function BeginFill(aCallback: TROAsyncCallback; aUserData: Pointer; aTables: array of TDADataTable; aSaveCursor: Boolean = false; aIncludeSchema: Boolean = false; aAppendMode: Boolean = False): IROAsyncRequest;overload;
    procedure EndFill(const aRequest: IROAsyncRequest);

    function BeginFillScripts(aCallback: TROAsyncCallback; aUserData: Pointer; aTables: array of TDADataTable): IROAsyncRequest;
    procedure EndFillScripts(const aRequest: IROAsyncRequest);

    function BeginFillSchema(aCallback: TROAsyncCallback; aUserData: Pointer; aTables: array of TDADataTable; aPreserveLookupFields: boolean = false; aPreserveClientCalcFields : boolean = false): IROAsyncRequest;
    procedure EndFillSchema(const aRequest: IROAsyncRequest);

    function BeginApplyUpdates(aCallback: TROAsyncCallback; aUserData: Pointer; aTables: array of TDADataTable): IROAsyncRequest;
    procedure EndApplyUpdates(const aRequest: IROAsyncRequest);

    function BeginLogin(aCallback: TROAsyncCallback; aUserData: Pointer): IROAsyncRequest;overload;
    function BeginLogin(aCallback: TROAsyncCallback; aUserData: Pointer; aLoginString: String): IROAsyncRequest;overload;
    function BeginLogin(aCallback: TROAsyncCallback; aUserData: Pointer; aUserName, aPassword: String): IROAsyncRequest; overload;
    function EndLogin(const aRequest: IROAsyncRequest): Boolean;

    function BeginLogout(aCallback: TROAsyncCallback; aUserData: Pointer): IROAsyncRequest;
    procedure EndLogout(const aRequest: IROAsyncRequest);

for start request, you need to call Begin* method. in callback method, you need to call correspondent End* method.

simple example
lRequest := RemoteDataAdapter.BeginFill(mycallback, nil, [table1, table2]);

in mycallback, which will be fired after BeginFill was finished, you need to have something like

procedure TClientForm.mycallback(const aRequest: IROAsyncRequest);
begin
  if (not aRequest.Cancelled) then begin
    RemoteDataAdapter.EndFill(aRequest);
    ShowMessage('Tables were loaded');
  end
  else 
    ShowMessage('Request was cancelled!');
end;

for cancelling async requests on client-side, you can call lRequest.Cancel;. if Request wasn’t sent to server yet, it will be cancelled, otherwise it will be cancelled after server’s response.

for cancelling requests that are already executed, and your driver supports this feature, you need to call a custom method in which you can cancel request.
For DOA driver, it can be reached via calling TOracleSession.BreakExecution on server-side.

1 Like

Will try it and tell you. How to pass Dynamic Where in the request?
Update. see in the first call, forget the question.

I cant see that method. Wich DA version is required?

it was added into DAD 8.3.93.1183 (September of 2015).

OK, Solved,

Now i cant find the way to fill the TDAWhereExpressionArray.

Now i have a simple dynamicwhere like that:

dw := TDADataSource(VistadeTabla.DataController.DataSource).DataTable.DynamicWhere;
dw.Clear;
...
    dw.Expression  := dw.NewBinaryExpressionList(aExp, dboAnd);
// open the table
TDADataSource(VistadeTabla.DataController.DataSource).DataTable.ACTIVE := TRUE

How to fill the dynamicwhere array?

if you don’t specify TDAWhereExpressionArray, dynamic where will be taken from table.

these calls are equal:

lRequest := RemoteDataAdapter.BeginFill(mycallback, nil, [table1]);
lRequest := RemoteDataAdapter.BeginFill(mycallback, nil, [table1],[table1.DynamicWhere.Expression]);

Excellent work Evgeny, Thanks!

Evgeny, more question.

One, is obvious the datatables must be in the same remotedataadapter, same dataservice , same schema. Wich is the best way to manage this when you have master-detail and each is on a different schema, dataservice, remotedataadapter?

I getting an error: “Parameter is incorrect” and detail table show no records. after i browse master works perfect. Any idea how to hide that error?

AFAIK detail data is requested when you move in master record. Dont get then how async work in that case. Or always the detail table is retrieved complete to the client side?

Best regards

master/details should work w/o any problem in case master and detail have different RDA

can you create a simple testcase that demonstrates this error, pls?
we have no Parameter is incorrect exception so it may raised in std TDataset

async calls should be called manually. when you move in master record usual call is fired.
but you can play with TDAMasterOption.moAllInOneFetch and TDADetailOption.dtIncludeInAllInOneFetch options. in this case, all details records will be downloaded in one call.
another solution: open detail table w/o m/d relation and later assign it. in this case, also whole table will be downloaded