Firstly, I’m ashamed that the Windows Phone team didn’t do a better job of providing an ability to set a Timeout for either HttpWebRequest, WebClient and WCF service requests. You might be thinking…. but it’s Silverlight and everything’s Async all the time. Well you’d be correct in that thought process but just because something is asynchronous, doesn’t mean that the user isn’t sitting there waiting for something to complete.
For example say you have a simple form that the user has to fill in before they can proceed (eg “create user” form). Clearly if they are on a good network then you’d hope that the service request returns almost instantaneously and the user is cleared to proceed within the app. However, if for whatever reason the request takes longer than expected (slow network, busy server etc) you don’t want the user to sit there indefinitely. After about 10-20 seconds they’re going to get frustrated and probably close and then uninstall your application. Simple solution is to put a Timeout on your service calls so that you can notify that there is something wrong and that they should try again later or check their connection.
Simple you say…. not so. There is no Timeout property that you can set on either HttpWebRequest, WebClient or on the WCF proxy classes that are generated by Add Service Reference. Luckily there are a number of good posts already on the web that cover the basics of implementing a timeout for doing HttpWebRequests (for example this post on stackoverflow).
What isn’t as well covered is how to implement a Timeout for WCF service calls. The challenge with implementing a Timeout property is trying to wrap the asynchronous pattern that the WCF proxy generates. For example if you have a service method called “Authenticate” the proxy will have a AuthenticateAsync method and an AuthenticateCompleted event. Attempting to use these to implement a Timeout is going to result in some very messy code. The other thing to consider is that .NET is evolving towards having true asynchronous support in the form of asynchronous method support. This is built in for WinRT and can be incorporated into your Windows Phone applications today using the Async CTP.
In this post we’re going to use the Async CTP to do two things. Firstly, wrap the fictitious Authenticate service call into an asynchronous operation and secondly give it Timeout support, again through the use of an async operation. The code is implemented as a partial class to the proxy class that is generated when you do “Add Service Reference”. It does required you to implement the first method for each service method that you want to wrap.
namespace PhoneApp30.MyServices
{
public partial class SimpleServiceClient {
public async Task<string> Authenticate(string username, string password, object state = null)
{
return await Async( (cb,st)=>
Channel.BeginAuthenticate(username,password,cb,st),
Channel.EndAuthenticate, state);
}
private async Task<TResult> Async<TResult>(
Func<AsyncCallback, object, IAsyncResult> beginMethod,
Func<IAsyncResult, TResult> endMethod,
object state)
{
var method = Task<TResult>.Factory.FromAsync(
beginMethod,
endMethod, state);
return await InvokeWithTimeout(method);
}
public async Task<TResult> InvokeWithTimeout<TResult>(Task<TResult> method)
{
var newTask = Task.Factory.StartNew(() =>
{
if (!method.Wait(30*1000))
{
Abort();
throw new TimeoutException();
}
return method.Result;
});
return await newTask;
}
}
}
Now we can call the Authenticate method from our code in a relatively synchronous manner. Note the difference is that this won’t block the UI as it is an asynchronous operation.
async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
try{
var client = new SimpleServiceClient();
var result = await client.Authenticate();
Console.WriteLine(result);
}
catch(TimeoutException ex){
// Handle timeout
}
}