Udostępnij za pośrednictwem


Simplified Asynchronous Programming Model in WCF with async/await

Managing multiple asynchronous operations in WCF is currently very complex regardless of whether you use the existing event or Begin/End asynchronous patterns. In fact, both internal and external customers frequently ask about how to implement simple coordination of WCF operations, and even though sending multiple requests to a WCF backend is a core scenario, WCF does not provide a satisfactory solution. The amount of code needed to facilitate even simple coordination tasks is large and prone to bugs, handling errors and timeouts. Yet there are some very common communication-oriented scenarios that require managing multiple outstanding asynchronous operations:

  • Execute multiple async operations in parallel and continue when they are all done, either successfully, failed, or timed out.
  • Execute sequence of asynchronous operations, stopping if one of the operations fails or times out.
  • Nest multiple asynchronous operations.
  • Combine asynchronous operations with timers for easy polling at regular intervals.

However, regardless of the current complexity, WCF developers have to use asynchronous requests to write robust implementations, and doing that today is a difficult challenge.

During PDC’10, the C# and VB.NET teams released the Visual Studio Async CTP, which aims at providing a new simplified model for doing asynchronous programming in both languages. In WCF, we have decided to adopt this model because we believe it removes the barrier to writing WCF robust asynchronous application, has the least possible learning curve from the existing CLR APM pattern, and provides a simple and consistent model for coordinating, submitting and managing multiple asynchronous WCF operations.

WCF vNext will adopt the Async model in both the client and the server side, and provide new Task-based overloads for some of the most used asynchronous APIs:

Server Side

WCF service developers will be able to define asynchronous operation contracts that return Task/Task<T>:

 [ServiceContract]
public interface IServiceContract
{
    [OperationContract]
    Task<string> HelloAsync(string name);
}

As you may notice, there is no need to switch OperationContract.AsyncPattern to true anymore (yeah!), or define Begin/End methods in your service contract.

In the service contract implementation, WCF service developers will be able to implement that operation contract as an async method, and create/start new tasks, etc.:

 namespace WCFServiceModelSamples
{
    public class HelloService : IServiceContract
    {
        public async Task<string> HelloAsync(string name)
        {
            return await Task.Factory.StartNew(() => "hello " + name);
        }
    }
}

There is no need to explicitly implement any Begin/End method, IAsyncResult objects or any type of callback. Internally, WCF will add an operation invoker to the dispatcher that will be able to process Task-based operations, which will make all work transparently to the user.

Client side

The support for generating Task-based operations on the client side will be available to users who rely on proxies generated by our client generation tools (svcutil.exe or Add Service Reference), as well as to users who prefer to directly use ChannelFactory<T>.

The client proxy generation tools will generate Task-based overloads for all operations in the contract by default. This means that WCF client application developers will not need to switch any new knob in the tools to be able to use this model. But they will always be able to opt-out with the /syncOnly switch if they wish. The output of executing ’svcutil.exe /async’ will still generate Begin/End methods, as it does today. WCF users should not experience any breaking change in their code. Finally, it is worth mentioning that WCF developers will be able to generate Task-based overloads as well by using ServiceContractGenerator.

From the implementation side, the support for executing Tasks will be implemented directly in the channel, which makes the generated client proxy code look as clean as this:

 public partial class HelloServiceClient : ClientBase<IServiceContract>,
                                          IServiceContract {
    public System.Threading.Tasks.Task<string> HelloAsync(int value) {
        return base.Channel.HelloAsync(value);
    }
    ...
} 

Finally, a code snippet for a WCF client application calling asynchronously the WCF service defined above will look like the following:

 namespace MyAsyncApplication
{
    class AsyncClientApp
    {
        static void Main(string[] args)
        {
            string[] names = { "daniel", "john", "tarae" };

            PrintHelloNames(names)
                .ContinueWith(delegate { Console.WriteLine("Done."); });

            Console.WriteLine("Hit ENTER to exit.\n");
            Console.ReadLine();
        }

        static async Task PrintHelloNames(string[] names)
        {
            foreach (string name in names)
            {
                try
                {
                    Task<string> task = new HelloServiceClient().HelloAsync(name);
                    if (task == await Task.WhenAny(task, Task.Delay(1000)))
                    {
                        Console.WriteLine(await task);
                    }
                    else Console.WriteLine("Timed out");
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }
    }
}

New Task-based public APIs

Even though WCF developers will be able to add their own Task-based wrappers for existing asynchronous APIs based on the Begin/End model via Task.Factory.FromAsync, WCF will provide Task-based overloads for some of the main asynchronous public APIs. In particular, for those in the MEX and Discovery client, for consistency with the tools-generated clients:

  • System.ServiceModel.Description.MetadataExchangeClient.BeginGetMetadataAsync
  • System.ServiceModel.Discovery.AnnouncementClient.BeginAnnounceOnlineAsync
  • System.ServiceModel.Discovery.AnnouncementClient.BeginAnnounceOfflineAsync
  • System.ServiceModel.Discovery.DiscoveryClient.FindTaskAsync
  • System.ServiceModel.Discovery.DiscoveryClient.ResolveTaskAsync

 

That’s all folks! Feedback is always encouraged. Please let us know what you think about this new programming model in WCF. Welcome to the new asynchronous programming world!

Amadeo Casas Cuadrado

Program Manager, WCF

Comments

  • Anonymous
    November 12, 2010
    Wow! nice article! I've been searching for this topic for a while and this article just clarified most of my doubts. Thank you, Casas

  • Anonymous
    November 12, 2010
    The comment has been removed

  • Anonymous
    November 14, 2010
    The comment has been removed

  • Anonymous
    November 17, 2010
    The comment has been removed

  • Anonymous
    November 18, 2010
    The comment has been removed

  • Anonymous
    November 21, 2010
    The comment has been removed

  • Anonymous
    November 21, 2010
    Now Async is devloper friendly. Thanks for sharing.

  • Anonymous
    November 23, 2010
    Hi, When, inside of Task, we have an I/O operation (database call), will this task run on IOCP thread?!

  • Anonymous
    November 23, 2010
    Yes, I/O operation will run on IOCP thread.