Tuesday, April 12, 2011

Async C# 5: Using Async Pattern with WCF and Silverlight

Sometime ago I published a way to use WCF service with the new Async Pattern in C#. The problem is that this approach won’t work with Silverlight. More specifically, TaskFactory is not available in Silverlight.

So.. here is one solution (it does not provide full functionality as in TaskFactory, but you can manage 90% of use cases):

MyTaskFactory
public static class MyTaskFactory
{
    public static Task<TResult> FromAsync<TResult>(
        IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod)
    {
        var completionSource = new TaskCompletionSource<TResult>();
        if (asyncResult.IsCompleted)
        {
            completionSource.TrySetResult(asyncResult, endMethod);
        }
        else
        {
            System.Threading.ThreadPool.RegisterWaitForSingleObject(
                asyncResult.AsyncWaitHandle,
                (state, timeOut) =>
                {
                    completionSource.TrySetResult(asyncResult, endMethod);
                }, null, -1, true);
        }

        return completionSource.Task;
    }

    static void TrySetResult<TResult>(
        this TaskCompletionSource<TResult> completionSource,
        IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod)
    {
        try
        {
            var result = endMethod(asyncResult);
            completionSource.TrySetResult(result);
        }
        catch (OperationCanceledException)
        {
            completionSource.TrySetCanceled();
        }
        catch (Exception genericException)
        {
            completionSource.TrySetException(genericException);
        }
    }
}

The previous code fragment defines a helper method that behaves similar to TaskFactory.

Usage example:

Usage:
var client = new MyServiceClient();
// EchoTaskAsync is an extension method
var result = await client.EchoTaskAsync("zzzz");

And here is the service extension for MyServiceClient (Note that I use the service interface contract instead of the ClientBase<> as parameter since we need access to Begin/End methods):

Service Async Extensions
public static class ServiceExtensions
{
    public static Task<string> EchoTaskAsync(
        this IMyService client, string text)
    {
        return MyTaskFactory.FromAsync<string>(
            client.BeginEcho(text, null, null),
            ar => client.EndEcho(ar));
    }
}

Try it and let me know if it works for you..

No comments: