I'm currently implementing a System.Web.Http.IActionFilter
which calls an internal service to determine whether the current request can continue. The problem I'm having is returning a Task<T1>
based on a piece of logic encapsulated by a Task<T2>
.
An example might help.
The internal service API is implemented using Tasks. The logic is trivial using .NET 4.5's async/await:
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
UserAuthenticationResult authResult = await HitInternalServiceAsync();
if (!authResult.IsAuthenticated)
{
throw new HttpResponseException("User is not authenticated", HttpStatusCode.Unauthorized);
}
return await continuation();
}
However it is more difficult with the old Task API in .NET 4.0;
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
return HitInternalServiceAsync()
.ContinueWith(t1 => {
UserAuthenticationResult authResult = t1.Result;
if (!authResult.IsAuthenticated)
{
throw new HttpResponseException("User is not authenticated", HttpStatusCode.Unauthorized);
}
//Hack hack - this blocks the thread until the task retuned by continuation() completes
return continuation().Result;
});
}
The difficult part comes when the authentication check has succeeded - I then want to await the task returned by the continuation function.
Using .NET 4.0 it looks like I have explicitly block when waiting for the continuation()
task to complete, rather than instructing the Tasks API to automatically continue with the continuation()
task when my task is done.
The question: Is this the only way to implement this behaviour in .NET 4.0?
Given a sufficiently complicated internal service API, I can easily see the number of Tasks waiting on other Tasks multiplying quickly.
EDIT: Looks like the above 4.0 code isn't viable either - because the continuation lambda does not execute in the ASP.NET thread context services like HttpContext.Current are not available. A better implementation would be...
public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
Task<UserAuthenticationResult> authResultTask = HitInternalServiceAsync();
var authResult = authResultTask.Result;
if (!authResult.IsAuthenticated)
{
throw new HttpResponseException("User is not authenticated", HttpStatusCode.Unauthorized);
}
return continuation();
}