Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async WebApi ActionFilterAttribute. An asynchronous module or handler completed while an asynchronous operation was still pending

I understand await waits for a task (an awaitable) to complete. But I'm confused about what that actually means.

The code that doesn't work:

public async override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
    if (actionExecutedContext.Response.Content != null)
    {
        var responseContent = await actionExecutedContext.Response.Content.ReadAsStringAsync();
        DoSomething(responseContent);
    }
}

The code that does work:

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
    if (actionExecutedContext.Response.Content != null)
    {
        var responseContent = actionExecutedContext.Response.Content.ReadAsStringAsync().ContinueWith(
        task =>
        {
            DoSomething(task.Result);
        });
    }
}

Obviously the error message An asynchronous module or handler completed while an asynchronous operation was still pending. tells me that there was no waiting for the async call to complete but instead the "main" thread continued. I expected the thread to continue but not within the current method. I thought the thread would return to the asp.net stack do some other work and return once the await asyncOperation() operation completed.

I'm using await in other places too - (e.g. waiting for web service responses) - and I didn't run into similar problems anywhere. I wonder why the IActionFilterAttribute behaves differently. In fact my web service calls probably take way longer than reading the content of the response into a string.

Can someone please enlighten me? I have the feeling I didn't understand the concept.

like image 790
lapsus Avatar asked Mar 19 '13 15:03

lapsus


People also ask

What is asynchronous in Web API?

Asynchronous APIs are also known as async APIs. With an asynchronous process, the availability of a resource, service or data store may not be immediate. The API may have to wait for a backend response. These APIs may provide a callback notification to the requester when the requested resource is ready.

How does async await work in Web API?

The async/await feature solves three performance or scalability problems: They can make your application handle more users. If you have requests that access an external resource such as a database or a web API then async frees up the thread while it is waiting.


2 Answers

Instead of implementing

public async override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)

you have to implement the async version of OnActionExecuted method as follows:

public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)

This way you can use await inside a method and behavior will be as you expected.

Hope this helps.

like image 178
Mikhail Baleika Avatar answered Sep 28 '22 10:09

Mikhail Baleika


Adding async code to a method that returns void is dangerous and almost never what you actually want to do. See What's the difference between returning void and returning a Task?.

Instead, you need to override/implement a method that returns a task. In this case, ActionFilterAttribute hides the Task that IHttpActionFilter provides, so you'll need to implement IActionFilter (ExecuteActionFilterAsync) instead. If you want to use you code as an attribute, just make sure you also derive from the Attribute class.

For example:

public class AsyncActionFilterAttribute : Attribute, IActionFilter
{
    public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
    {
        HttpResponseMessage response = await continuation();
        DoSomething(response);
        return response;
    }
}
like image 42
dmatson Avatar answered Sep 28 '22 08:09

dmatson