views:

60

answers:

2

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);
        }
    }
+1  A: 

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, ...).

Darin Dimitrov
+1 makes no sense whatsoever - totally agree
marc_s
The thing is that the WCF service seems to only have async methods (GeocodeAsync()), so I seem to be stuck doing that asynchronously, that's the reason I want it to finish before I can move on. I'm trying it in the Completed event and see if it works from there.
Carlo
Btw, the ManualResetEvent didn't work, it first waits the 2 minutes, then calls the Completed event.
Carlo
+1  A: 

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.

Andrew Shepherd
I agree, much simpler than using a ManualResetEvent. You are not gauranteed that the web service has returned a result using a manual reset, but you are with the End call.
Moderator71
Ohh I really like this. I will give it a shot. Thanks!
Carlo
Hmm only in this case, GeocodeAsync returns void.
Carlo
Hmmm, I was wondering how GeocodeAsync came about. It doesn't seem to be following the usual asynchronous pattern. I'll add more to my answer.
Andrew Shepherd