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.
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.
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.
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.
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With