An asynchronous service using async-await is very responsive as it can interleave many client calls and execute them in parallel (2).
Despite this, the service can run fully thread-safe on one thread (3) and can be a singleton service (1) or a service object created by the framework for a session or a call only.
When implementing the service, please note the ServiceBehaviourAttributes (1)...(3) :
[ServiceContract( Namespace="X", Name="TheContract" )]
public interface IAsyncContractForClientAndService
{
[OperationContract]
Task<TResponse> SendReceiveAsync( TRequest req );
}
[ServiceBehavior (InstanceContextMode = InstanceContextMode.Single, // (1)
// also works with InstanceContextMode.PerSession or PerCall
ConcurrencyMode = ConcurrencyMode.Multiple, // (2)
UseSynchronizationContext = true)] // (3)
public MyService : IAsyncContractForClientAndService
{
public async Task<TResponse> SendReceiveAsync( TRequest req )
{
DoSomethingSynchronous();
await SomethingAsynchronous();
// await lets other clients call the service here or at any await in
// subfunctions. Calls from clients execute 'interleaved'.
return new TResponse( ... );
}
}
To run every call on one thread, a System.Threading.SynchronizationContext.Current != null must be present at the moment you Open() the ServiceHost.
Using the SynchronizationContext, you need not to care about locks. Atomic, non interruptable code sections stretch roughly from one await to the next.
Take care that shared service data is in a consistent state at every await and be aware that successive requests from one client may be responded not in the order they where sent.
On client side, the asynchronous service operation is awaitable:
var response = await client.Channel.SendReceiveAsync( request );
It is not possible to use out or ref parameters in the operation contract. All response data must be passed by the returned value Task(T).
I use this interface in AsyncWcfLib, it supports a Actor based programming model.