Building an Async WCF Service in .NET 4.5 | Howard Dierking
In the .NET Framework 4.0 version of WCF, managing multiple asynchronous operations, for example in the form of WCF and HTTP request/response operations, is currently very complex regardless of whether you use the existing event or Begin/End asynchronous pattern. 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 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 async operations, stopping if one of the operations fails or times out.
- Nest multiple async operations (first do A, then take result and feed to B etc.).
- Combine async operations with timers for easy polling at regular intervals.
The task-based asynchronous programming model first introduced in the .NET Framework 4.0 provides a simple abstraction for the definition, coordination, and management of these operations. As such, providing a first-class asynchronous development experience using tasks should make the above scenarios much simpler to realize.
In order to create an asynchronous service operation, the service developer need only define a service operation that returns an instance of either Task or Task<T>. The decision on whether to return Task or Task<T> depends on whether or not the service operation returns a value. For operations which specify void, Task should be retuned. For service operations that return a value, Task<T> should be retuned.
For task-based methods, if the OperationContractAttribute specifies a name, the name will be used as is. If no name is specified on the OperationContractAttribute, the method name will be used. If the method name terminates with the string “Async”, “Async” will be truncated from the method name and the resulting string will be used as the operation name. This is to avoid having a method like “FooAsyncAsync” on the client after proxy code generation.
If a service like the one below is written, an InvalidOperationException will be thrown because the task-based method has the same name as the sync and async methods. As a result, a service of the type below would not be supported.
|[ServiceContractAttribute(Namespace = "http://microsoft.samples")]|
|public interface ISampleService|
|string SampleMethod(string msg);|
|[OperationContractAttribute(AsyncPattern = true)]|
|IAsyncResult BeginSampleMethod(string msg, AsyncCallback callback,|
|string EndSampleMethod(IAsyncResult result);|
|[OperationContractAttribute(Name = "SampleMethod")]|
|Task<string> SampleMethodAsync(string msg);|
The following illustrates use of a task-basd asynchronous service operation which in turns calls out asynchronously to 2 external services, waits for both services to return, then calculates and returns a value based on the return values of the 2 external services.
This service operation leverages the Task.WhenAll combinator in order to wait for the completion of both asynchronous external service calls in a manner that does not block the thread of execution. The C# 5 async and await keywords are then used to return identify the service operation as asynchronous and define the operation which adds the product unit price and the shipping price as a continuation.
After proxy generation, calling the service looks boringly good: