Select to view content in your preferred language

Synchronous method for QueryTask?

3542
4
02-07-2012 01:47 PM
MikeDuppong
Emerging Contributor
QueryTask has an ExecuteAsync() method, but I need a synchronous version.  I have to pull in all the features of a layer before proceeding further with the program.

How can I achieve this?
0 Kudos
4 Replies
JenniferNery
Esri Regular Contributor
QueryTask is really a synchronous operation on the server but Silverlight web requests are asynchronous, which means you need ExecuteCompleted event to get the result. http://help.arcgis.com/en/webapi/silverlight/apiref/ESRI.ArcGIS.Client~ESRI.ArcGIS.Client.Tasks.Quer.... If you are using WPF, however, you can get FeatureSet from queryTask.Execute.
http://help.arcgis.com/en/webapi/wpf/apiref/ESRI.ArcGIS.Client~ESRI.ArcGIS.Client.Tasks.QueryTask~Ex...
0 Kudos
SergeyZorin
New Contributor
You can "emulate" synchronous call using ManualResetEvent.
Here is code from my project (modified it a bit to remove project-specific code). It works for spatial query but you can always modify it to use "where" clause.
public FeatureSet Query(Geometry geometry, string layerURL)
{
    if (Dispatcher.CheckAccess())
    {
        var message = string.Format("\"Query\" method should be called on background thread.");
        throw new Exception(message);
    }

    FeatureSet result = null;
    Exception error = null;

    var manualResetEvent = new ManualResetEvent(false);

    Dispatcher.Invoke(() =>
    {
        var queryTask = new QueryTask(layerURL);                

        var query = new Query();
        query.OutFields.Add("*");
        query.Geometry = geometry;
        query.ReturnGeometry = true;
        query.OutSpatialReference = Map.SpatialReference;

        EventHandler<TaskFailedEventArgs> onTaskFailed = null;
        EventHandler<QueryEventArgs> onExecuteCompleted = null;

        onTaskFailed = (s, a) =>
        {
            queryTask.ExecuteCompleted -= onExecuteCompleted;
            queryTask.Failed -= onTaskFailed;
            error = a.Error;
            manualResetEvent.Set();
        };

        onExecuteCompleted = (s, a) =>
        {
            queryTask.ExecuteCompleted -= onExecuteCompleted;
            queryTask.Failed -= onTaskFailed;
            result = a.FeatureSet;
            manualResetEvent.Set();
        };

        queryTask.ExecuteCompleted += onExecuteCompleted;
        queryTask.Failed += onTaskFailed;
        queryTask.ExecuteAsync(query);
    });

    manualResetEvent.WaitOne();
            
    if (error == null)
    {
        return result;
    }
    else
    {
        throw error;
    }
}


You can get Dispatcher instance from any UI control, for example layout root.
Also you must call this method on background thread (exception on the first line is for a purpose) because manual reset event will block UI thread and entire application. You can do this by using BackgroundWorker:

backgroundWorker.DoWork += (s, a) =>
{
    [your code here]
    var featureSet = Query(geometry, layerURL);
    [your code here using featureSet]
    var maybeAnotherFeatureSet = Query(geometry2, layerURL2);
    [you code here using both feature sets]
};

backgroundWorker.RunWorkerCompleted += (s, a) =>
{
    [output results and throw errors here]
};

backgroundWorker.RunWorkerAsync();

Using this approach you can group and chain asynchronous calls into one asynchronous background worker call.

P.S. so much code/text here, now I think it can be overkill for your problem.
0 Kudos
SanajyJadhav
Deactivated User
I am not sure but can't you use FeatureLayer with mode "Selection"?
0 Kudos
MikeDuppong
Emerging Contributor
Thank you, Surgey - I was actually working on a very similar solution, but I'm taking some pointers with your example.

I appreciate your time and help.

Mike
0 Kudos