unit FetchClientMain; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, uROClient, uROClientIntf, uRORemoteService, uROBinMessage, ExtCtrls, Spin, Grids, DBGrids, ComCtrls, DB, uDAInterfaces, uDADataTable, uDAScriptingProvider, uDAMemDataTable, uDARemoteDataAdapter; type TFetchClientMainForm = class(TForm) PageControl1: TPageControl; TabSheet1: TTabSheet; Label1: TLabel; lbFetchedRecs: TLabel; gPagedOrders: TDBGrid; seMaxRecords: TSpinEdit; bFetchPagedOrders: TButton; gPagedOrderDetails: TDBGrid; Panel1: TPanel; TabSheet2: TTabSheet; Label2: TLabel; Label3: TLabel; gOrders: TDBGrid; gOrderDetails: TDBGrid; bFetchAll: TButton; seStart: TSpinEdit; seEnd: TSpinEdit; Panel2: TPanel; daPaged: TDARemoteDataAdapter; tbl_OrderDetails: TDAMemDataTable; ds_OrderDetails: TDADataSource; tbl_Orders: TDAMemDataTable; ds_Orders: TDADataSource; tbl_PagedOrderDetails: TDAMemDataTable; ds_PagedOrderDetails: TDADataSource; tbl_PagedOrders: TDAMemDataTable; ds_PagedOrders: TDADataSource; daFetchAll: TDARemoteDataAdapter; procedure bFetchPagedOrdersClick(Sender: TObject); procedure bFetchAllClick(Sender: TObject); procedure tbl_PagedOrdersAfterScroll(DataTable: TDADataTable); private { Private declarations } fFetchRequired: boolean; public { Public declarations } end; var FetchClientMainForm: TFetchClientMainForm; implementation uses FetchClientData; {$R *.dfm} procedure TFetchClientMainForm.bFetchAllClick(Sender: TObject); begin { Sets the filtering options for the server method so we only retrieve the selected records. It's good advice to always filter data like this to minimize network trafic. The server method is defined as: function GetOrdersAndDetails(const StartOrderID: Integer; const EndOrderID: Integer): Binary; The Binary returned by the server contains a stream with all the orders and all the details } daFetchAll.GetDataCall.ParamByName('StartOrderID').AsInteger := seStart.Value; daFetchAll.GetDataCall.ParamByName('EndOrderID').AsInteger := seEnd.Value; tbl_Orders.Active :=not tbl_Orders.Active; end; procedure TFetchClientMainForm.bFetchPagedOrdersClick(Sender: TObject); begin lbFetchedRecs.Caption := 'No records fetched yet...'; if not tbl_PagedOrders.Active then begin tbl_PagedOrders.ParamByName('StartingOrderID').AsInteger := 0; tbl_PagedOrders.MaxRecords := seMaxRecords.Value; lbFetchedRecs.Caption := 'No records fetched yet...'; // fFetchedRequired is a flag that we set to FALSE when the last page is fetched fFetchRequired := True; end; tbl_PagedOrders.Active := not tbl_PagedOrders.Active end; procedure TFetchClientMainForm.tbl_PagedOrdersAfterScroll( DataTable: TDADataTable); var lrecordcount: integer; begin { This is the core of the paging process. When we reach the last record in the current set we want to issue a new request to the server to get the next packet. If the requested records is different than the returned amount it means there's nothing more to fetch and we're done with the paging. By doing paging this way we have full control on what happens server side and what SQL statements are generated. This is only one of the many possible paging implementations you could do using Data Abstract. The control is in your hands! } if fFetchRequired and DataTable.EOF and not DataTable.Fetching then with DataTable do begin ParamByName('StartingOrderID').AsInteger := FieldByName('OrderID').AsInteger; MaxRecords := seMaxRecords.Value; lrecordcount := DataTable.RecordCount; LoadFromRemoteSource(True); lrecordcount := RecordCount-lrecordcount; lbFetchedRecs.Caption := IntTostr(lrecordcount) + ' records retrieved'; fFetchRequired := lrecordcount = MaxRecords; if not fFetchRequired then MessageDlg(Format('Done fetching!!! Expected %d rows but only got %d ', [MaxRecords, lrecordcount]), mtInformation, [mbOK], 0); end; end; end.