How to: Implement a WCF Asynchronous Service Operation with Task

I love Task<T>.  It has to be one of the finest innovations in the framework in the past few years.  Recently I was reviewing some old WCF documentation How to: Implement an Asynchronous Service Operation which was probably written in 2006 and I have to say that we can do a much better job of it with Task<T> so here goes my rewrite.

Download Sample Code How to: Implement and Consume a WCF Asynchronous Service Operation with Task<T>

Implement a service operation asynchronously

  1. In your service contract, declare an asynchronous method pair according to the .NET asynchronous design guidelines. The Begin method takes a parameter, a callback object, and a state object, and returns a System.IAsyncResult and a matching End method that takes a System.IAsyncResult and returns the return value. For more information about asynchronous calls, see Asynchronous Programming Design Patterns.

  2. Mark the Begin method of the asynchronous method pair with the System.ServiceModel.OperationContractAttribute attribute and set the System.ServiceModel.OperationContractAttribute.AsyncPattern property to true. For example, the following code performs steps 1 and 2.

        1: [ServiceContract]
        2: public interface ISampleTaskAsync
        3: {
        4:     [OperationContract(AsyncPattern = true)]
        5:     IAsyncResult BeginDoWork(int count, AsyncCallback callback, object state);
        6:  
        7:     int EndDoWork(IAsyncResult result);
        8: }
    
  3. Implement the Begin/End method pair in your service class according to the asynchronous design guidelines. For example, the following code example shows an implementation in which a string is written to the console in both the Begin and End portions of the asynchronous service operation, and the return value of the End operation is returned to the client. For the complete code example, see the Example section.

Example

The following code examples show:

  1. A service contract interface with:

    • A synchronous SampleMethod operation.
    • An asynchronous BeginSampleMethod operation.
    • An asynchronous BeginServiceAsyncMethod/EndServiceAsyncMethod operation pair.
  2. A service implementation using a System.Threading.Tasks.Task object which implements IAsyncResult.

    1: public class SampleTaskAsync : ISampleTaskAsync
    2:     {
    3:         public int Count { get; set; }
    4:  
    5:         #region ISampleTaskAsync Members
    6:  
    7:         public IAsyncResult BeginDoWork(int count, AsyncCallback callback, object state)
    8:         {
    9:             this.Count = count;
   10:  
   11:             // Create a task to do the work
   12:             var task = Task<int>.Factory.StartNew(this.WorkerFunction, state);
   13:  
   14:             return task.ContinueWith(res => callback(task));
   15:         }
   16:  
   17:         public int EndDoWork(IAsyncResult result)
   18:         {
   19:             // If an exception occured in your worker it will show up here
   20:             return ((Task<int>)result).Result;
   21:         }
   22:  
   23:         #endregion
   24:  
   25:         /// <summary>
   26:         ///   A worker function that does the actual work
   27:         /// </summary>
   28:         /// <returns>The result you want to return to the caller</returns>
   29:         private int WorkerFunction(object state)
   30:         {
   31:             var sum = 0;
   32:             for (var i = 0; i < this.Count; i++)
   33:             {
   34:                 sum += i;
   35:                 Thread.Sleep(10);
   36:             }
   37:  
   38:             return sum;
   39:         }
   40:     }

How to: Call WCF Service Operations Asynchronously

  1. Add a Service Reference to your WCF Service

  2. Configure the Service Reference to Generate Async Operations

  3. Use Task<T>.Factory.FromAsync to consume the service as shown

    1: public ActionResult Test()
    2: {
    3:     // Be sure to configure your proxy for Asynchronous Operations
    4:     var proxy = new Services.SampleTaskAsyncClient();
    5:  
    6:     object state = "This can be whatever you want it to be";
    7:  
    8:     // Parameters that you pass to the BeginDoWork method go right after EndDoWork.
    9:     var task = Task<int>.Factory.FromAsync(proxy.BeginDoWork, proxy.EndDoWork, 10, state);
   10:  
   11:     // You can do other things while the proxy is waiting for the service call
   12:     var sum = 0;
   13:  
   14:     for (var i = 0; i < 100; i++)
   15:     {
   16:         sum = sum + i;
   17:     }
   18:  
   19:     // When you are done, access the Result.
   20:     // If the call is not finished, it will block until the result is ready
   21:     ViewData["Result"] = task.Result;
   22:     ViewData["Sum"] = sum;
   23:  
   24:     proxy.Close();
   25:  
   26:     return View();
   27: }