February 26, 2011

Async Web Service Timout

When using SOAP-based web services (in this case with MonoDevelop / MonoTouch) I found I had to rely on the asynchronous proxy web methods to keep my UI responsive. Pretty common, right? Unfortunately, the asynchronous web methods seem to ignore the TimeOut property on the service. Without a timeout my users could be left waiting indefinitely on an unresponsive server. Not acceptable.

To get around this issue I came up with a way to cancel an asynchronous web method request after a set period of time using a Timer object. Maybe it can help others in a similar predicament? Here's some sample code:

protected void GetServiceData()
{
//Indicates that network activity is going on
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true;

//Make the async call
using (MyService service = new MyService())
{
//Timer is set to go off one time after 15 seconds
Timer serviceTimer = new Timer(15000);
serviceTimer.AutoReset = false;
serviceTimer.Elapsed += delegate(object source, ElapsedEventArgs e) {
service.Abort();
throw new WebException("Timeout expired!");
};
serviceTimer.Enabled = true;

//Call the desired web method
service.WebMethodCompleted += ServiceWebMethodCompleted;
service.WebMethodAsync(serviceTimer);
}
}

//The async callback method
protected void ServiceWebMethodCompleted(object sender, WebMethodCompletedEventArgs e)
{
using (NSAutoreleasePool pool = new NSAutoreleasePool())
{
//Disable the timer that would abort this call with an exception
//if the call to this web method took too long
Timer serviceTimer = e.UserState as Timer;
if (serviceTimer != null)
{
serviceTimer.Enabled = false;
serviceTimer.Dispose();
}

if (e.Error != null)
{
if (e.Error is WebException)
{
//An error due to a timeout happened - handle it here
}
else
{
//Handle all other errors here
}
}
else
{
//Async call successful - do something cool with e.Result
}

//Indicates network activity has finished
this.InvokeOnMainThread(delegate() {
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false;
});
}
}

3 comments:

Anonymous said...

Thanks for sharing. This was just what I was looking form. Excellent.

@ArranM

Carlos Bustos said...

Great article! but my resharper is showing a warning: "access to diposed closure", inside of serviceTimer.Elapsed
in line service.Abort()

How could I fix this? thx

Mauro Cavallin said...

Thank you: an ispirational post still today, after some time.

Seem that Monotouch WebService-Enabled app needed tho watchDogTimer beacause we can't override default 100 seconds timeout of Monotouch 2.0 WebServices. Sometimes the Framework (or the O.S.) understand there is no network connection so stops immediately the web service call but sometimes this doesn't happen (i.e. when you switch Airplane Mode on in the middle of calls).

A Few Observation: setting enabled = true to the timer does not start it: you have to start after that line.

The abort() command throws itself an Exception that comes out in the Completed event as an error, there is no need throw another.

Using this watchdogtimer solved all mi issues :-) thank you!