I am comfortable with executing synchonous work before calling the inner handler’s SendAsync(), and executing synchronous work after the inner handler has completed via a completion. e.g:
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// do some sync work before inner handler here
var sendTask = base.SendAsync(request, cancellationToken);
return sendTask.ContinueWith(
task => { // do some sync work afterwards here });
}
However, I now need to call an IO bound operation from within a delegating handler. The IO bound operation is already wrapped up as a Task<bool>. I need to use the result to determine whether to continue on to the inner handler.
An example would be making a network call to authorize a request. I have to do this to integrate with an existing system. In general, I think there are valid scenarios for this problem, and it should have a workable solution.
What’s the right way to implement SendAsync in this case, so that I execute the IO bound Task asynchronously and then continue to asynchronously execute the inner handler?
The key point is that I want to be certain that the request thread is not left blocked at any point.
OK, I think I have this cracked. I’m illustrating this with an authentication scenario: I want to authenticate a user asynchronously and use the result to decide whether to return a 401 or continue with the message handler chain.
The central problem is that you cannot call the inner handler SendAsync() until you have the result from the asynchronous authentication.
The key insight for me was to use a TaskCompletionSource (TCS) to control the flow of execution. This enabled me to return the Task from the TCS and set a result on it whenever I liked – and most importantly to delay the call the SendAsync() until I know I need it.
So, I set up the TCS and then started a task to do the authorization. In the continuation to this, I look at the result. If authorized, I invoke the inner handler chain and attach a continuation to this (avoiding any thread blocking) that completes the TCS. If authentication fails I just complete the TCS there and then with a 401.
The result of this is that both asynchronous tasks are executed in turn without any thread blocking. I load tested this and it seems to work just fine.
It’s all much nicer in .NET 4.5 with the async/await syntax though… although the approach with the TCS is still basically happening under the covers, the code is much simpler.
Enjoy!
The first snippet was built on .NET 4.0 with the Web API Beta – the second on .NET 4.5/Web API RC.
Here is a .NET 4.5 / Web API Release Candidate version which is a lot sexier with the new async/await syntax: