Carlo
Carlo

Reputation: 25959

How can I wait for an async WCF service to complete?

The question pretty much sums it up. I have a WCF service, and I want to wait until it finished to do something else, but it has to be until it finishes. My code looks something like this. Thanks!

    private void RequestGeoCoordinateFromAddress(string address)
    {
        GeocodeRequest geocodeRequest = new GeocodeRequest();

        GeocodeServiceClient geocodeService = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");

        geocodeService.GeocodeCompleted += new EventHandler<GeocodeCompletedEventArgs>(geocodeService_GeocodeCompleted);

        // Make the geocode request
        geocodeService.GeocodeAsync(geocodeRequest);

        //if (geocodeResponse.Results.Length > 0)
        //    results = String.Format("Latitude: {0}\nLongitude: {1}",
        //      geocodeResponse.Results[0].Locations[0].Latitude,
        //      geocodeResponse.Results[0].Locations[0].Longitude);
        //else
        //    results = "No Results Found";

        // wait for the request to finish here, so I can do something else
        // DoSomethingElse();
    }

    private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
    {
        bool isErrorNull = e.Error == null;
        Exception error = e.Error;

        try
        {
            double altitude = e.Result.Results[0].Locations[0].Latitude;
            double longitude = e.Result.Results[0].Locations[0].Longitude;

            SetMapLocation(new GeoCoordinate(altitude, longitude));
        }
        catch (Exception ex)
        {
            // TODO: Remove reason later
            MessageBox.Show("Unable to find address. Reason: " + ex.Message);
        }
    }

Upvotes: 2

Views: 11250

Answers (5)

Vitaliy Markitanov
Vitaliy Markitanov

Reputation: 2448

I saw one guy did use ManualReset and waitAll, but he had to wrap all code inside of ThreadPool.. It is very bad idea...thought it works

Upvotes: 0

Stefan Forster
Stefan Forster

Reputation: 425

The Visual Studio 11 Beta inludes C# 5 with async-await.

See Async CTP - How can I use async/await to call a wcf service?

It makes it possible to write async clients in a 'synchronous style'.

Upvotes: 0

abg
abg

Reputation: 2092

Don't use this code with Silverlight:

private ManualResetEvent _wait = new ManualResetEvent(false);

private void RequestGeoCoordinateFromAddress(string address)
{
    ...
    _wait = new ManualResetEvent(false);
    geocodeService.GeocodeAsync(geocodeRequest); 
    // wait for maximum 2 minutes
    _wait.WaitOne(TimeSpan.FromMinutes(2));
    // at that point the web service returned
}

private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
   ...
   _wait.Set();
}

When we call _wait.WaitOne(TimeSpan.FromMinutes(2)), we are blocking the UI thread, which means the service call never takes place. In the background, the call to geocodeService.GeocodeAsync is actually placed in a message queue, and will only be actioned when the thread is not executing user code. If we block the thread, the service call never takes place.

Synchronous Web Service Calls with Silverlight: Dispelling the async-only myth

Upvotes: 1

Andrew Shepherd
Andrew Shepherd

Reputation: 45252

There is a pattern, supported by WCF, for a call to have an asynchronous begin call, and a corresponding end call.

In this case, the asynchronous methods would be in the client's interface as so:

[ServiceContract]
interface GeocodeService
{
     // Synchronous Operations
     [OperationContract(AsyncPattern = false, Action="tempuri://Geocode", ReplyAction="GeocodeReply")]
     GeocodeResults Geocode(GeocodeRequestType geocodeRequest);

     // Asynchronous operations
     [OperationContract(AsyncPattern = true, Action="tempuri://Geocode", ReplyAction="GeocodeReply")]
     IAsyncResult BeginGeocode(GeocodeRequestType geocodeRequest, object asyncState);
     GeocodeResults EndGeocode(IAsyncResult result);
}

If you generate the client interface using svcutil with the asynchronous calls option, you will get all of this automatically. You can also hand-create the client interface if you aren't using automatically generating the client proxies.

The End call would block until the call is complete.

IAsyncResult asyncResult = geocodeService.BeginGeocode(geocodeRequest, null);
//
// Do something else with your CPU cycles here, if you want to
//

var geocodeResponse = geocodeService.EndGeocode(asyncResult); 

I don't know what you've done with your interface declarations to get the GeocodeAsync function, but if you can wrangle it back into this pattern your job would be easier.

Upvotes: 3

Darin Dimitrov
Darin Dimitrov

Reputation: 1038800

You could use a ManualResetEvent:

private ManualResetEvent _wait = new ManualResetEvent(false);

private void RequestGeoCoordinateFromAddress(string address)
{
    ...
    _wait = new ManualResetEvent(false);
    geocodeService.GeocodeAsync(geocodeRequest); 
    // wait for maximum 2 minutes
    _wait.WaitOne(TimeSpan.FromMinutes(2));
    // at that point the web service returned
}

private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
   ...
   _wait.Set();
}

Obviously doing this makes absolutely no sense, so the question here is: why do you need to do this? Why using async call if you are going to block the main thread? Why not use a direct call instead?

Generally when using async web service calls you shouldn't block the main thread but do all the work of handling the results in the async callback. Depending of the type of application (WinForms, WPF) you shouldn't forget that GUI controls can only be updated on the main thread so if you intend to modify the GUI in the callback you should use the appropriate technique (InvokeRequired, ...).

Upvotes: 1

Related Questions