I've recently just done my first project embracing the new async features of C# 5.0 and thought I'd share some of my experiences using it out in the real world.
The project I was building was a web application which uses RavenDB for most persistence tasks. which has full support for running all operations that run network commands asynchronously. This then leads to the ability to run a full, end-to-end asynchronous stack, meaning that threads can be much better utilised, as a method that is waiting on a slow operations such as communicating across a network, can release its thread while waiting for the operation to complete.
If a service method does not have a return type (returns void), then simply return a plain Task.
Async methods in this scenario should never return void, and should be considered an anti-pattern to avoid. More discussion is available in this MSDN article on Async Best Practices.
One aspect of service design which is affected by the move to an asynchronous stack is how you model exceptional results that can be handled by the web application.
Typically, with synchronous services, custom exception types can be a neat way of representing unsuccessful results, which can optionally be caught at the web project level to be displayed nicely. However, in an asynchronous world, exceptions pose more challenges for handling due to possibly being wrapped in an aggregate exception.
Because of this, I took the approach of using polymorphic result types to represent all possible types of result (apart from system failure). This works much like a discriminated union in F# the return type is simply a finite list of possible outcomes.
Using this kind of pattern, you can avoid using exceptions for any results that may want to be handled further down the stack.
Writing unit and integration tests around asynchronous methods was surprisingly straight forward with only a few minor problems (which may be fixed in future versions).
The first problem I ran into with NUnit was the lack of support for writing an asynchronous setup method. NUnit currently requires the setup method to have a void return type and, as mentioned earlier, using a void return type with asynchronous methods is not fun. It means that the setup might not have finished setting everything up at the point when it starts running the test, resulting in inconsistent test environments.
The first solution to this is to not mark the setup method as "async" and run all asynchronous code synchronously using Task.WaitAll(), or some similar method.
The second solution is to create an asynchronous setup method, but rather than adding the NUnit setup attribute, call it directly on the first line of each test.
Neither of these methods is ideal, though I'd tend to prefer the second method as this allows for the setup method to be parameterised.