Select to view content in your preferred language

QueryFeaturesAsync(), paging and Task.WhenAll() or similar

135
1
2 weeks ago
Labels (2)
Mike_Quetel
Occasional Contributor

It's very common for my app to make use of the paging capability of QueryFeaturesAsync(). I'm now interested in (hopefully) improving app performance when my query will require many pages. To me this means batching requests for pages using something like Task.WhenAll() or Parallel.ForEachAsync(). My problem is the number of pages is unknown so I don't know how to create an array of tasks to batch.

A little history:

When I initially implemented paging in my app, the recommendation was to establish the feature count using QueryFeatureCountAsync() then use the service's MaxRecordCount to calculate the number of pages required to return all the features that met the criteria. This info drove the paging logic and importantly for this question, the number of pages required was considered a known quantity. Later I realized that I often would not get all features back. I think this was due to implementation details/decisions made in the esri software stack and/or underlying RDBMS.

The esri recommendation changed to just keep requesting pages until the FeatureQueryResult.IsTransferLimitExceeded property returned false. This method works great and I do get all features that satisfy the criteria. What I don't know anymore, is how may pages this will take to accomplish.

My question is:

Does anyone know how to batch requests to QueryFeaturesAsync() given the indeterminate number of calls that will be needed to satisfy the criteria? Would love to hear from any esri devs that have suggestions, or anyone else that is going back and attempting to squeeze more performance out of their app.  As mentioned the mechanisms I'm using in other parts of my app include Task.WhenAll() and Parallel.ForEachAsync(), but I don't see how they could be useful in this case.

For posterity, here is an example of my current paging logic and it makes me sad that that I have to wait for each round-trip before dispatching another.

 

pageCount = 0;
bool continueRetrievingPages = true;
var serviceFeatureTable = this.FeatureTable as Esri.ArcGISRuntime.Data.ServiceFeatureTable;
while (continueRetrievingPages)
{
    queryParameters.ResultOffset = Convert.ToInt32(pageCount * pageSize);
    queryParameters.MaxFeatures = Convert.ToInt32(pageSize);
    this.CancelationTokenSource = new System.Threading.CancellationTokenSource(new TimeSpan(0, 0, 0, defaultTimeoutInSeconds, 0));
    var pageResult = await serviceFeatureTable.QueryFeaturesAsync(queryParameters, Esri.ArcGISRuntime.Data.QueryFeatureFields.LoadAll, this.CancelationTokenSource.Token);
    continueRetrievingPages = pageResult.IsTransferLimitExceeded;
    this.CancelationTokenSource = null;
    pageResults.Add(pageResult);
    pageCount++;
}

 

 

1 Reply
dotMorten_esri
Esri Notable Contributor

I'm not aware of a way to do this up-front in parallel. Chances are though that you won't be seeing a whole lot of performance improvement doing it in parallel.
This is a way to get all the object ids without hitting the server limit, but you'll need to call the rest endpoint directly. For example: https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer/1/query?where=1%3D1&ret...

I'm not sure what the scenario you're trying to solve that requires loading everything up front, but if it's for scrolling a list of features, some list controls have the ability to auto-query more features as you scroll closer to the bottom, and thus continue to fill up, like the ISupportIncrementalLoading interface.

Another good way to improve performance is to reduce the number of outfields you request to just the minimum you need for initial display. You can always load the feature layer to get its full set of attributes (if let's say the user clicks the item to get more information - this is the same approach the map uses by only pulling what it needs for the renderer and labels)

0 Kudos