13

I'm writing an Action Filter (inheriting from ActionFilterAttribute) which uses HttpClient to POST data to an external server in the OnResultExecuted method. HttpClient has the method PostAsync which returns an awaitable Task<HttpResponseMessage>.

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
    using (var client = new HttpClient())
    {
        var task = client.PostAsync(GetUri(), GetContent());
        var result = task.Result; // blocking
    }
}

The accepted answer to Async action filter in MVC 4 says it is not possible in MVC 4.

Is this still true in MVC 5, and if so what is the best way of calling this asynchronous method without blocking the thread?

Community
  • 1
  • 1
dav_i
  • 27,509
  • 17
  • 104
  • 136
  • use `await task` not the blocking call `.Result` – L.B Aug 11 '14 at 14:46
  • Will turning it into an `async` method just _work_? – dav_i Aug 11 '14 at 14:48
  • 1
    No, because then it won't technically be an override anymore (the method definition will be inherently different). It will just be a new method you're adding that will never be called by the framework. – Chris Pratt Aug 11 '14 at 14:54
  • Yeah, it seemed too easy a solution for that to work! – dav_i Aug 11 '14 at 14:55
  • @ChrisPratt addding just a `async` to a method doesn't change its signature. Try simply by adding async to Form_Load, for ex. – L.B Aug 11 '14 at 14:59
  • @dav_i I don't know whether MVC5 supports async, but how much time would it take to test it. – L.B Aug 11 '14 at 15:01
  • @L.B... no adding async alone doesn't change the method signature but you can't do that without *also* changing the return type to `Task` instead of `void` and that *will* change the signature. Otherwise, adding `async` doesn't actually do anything. The method will still be run sync. – Chris Pratt Aug 11 '14 at 15:03
  • 1
    @ChrisPratt As I said, test it..I did before posting previous comment. – L.B Aug 11 '14 at 15:03
  • 1
    The method will still be run sync unless it can return a `Task`. Adding `async` isn't a magic wand. – Chris Pratt Aug 11 '14 at 15:04
  • @ChrisPratt **Test it**. I did it by adding an `await Task.Delay(10000)` to Form_Load and worked as expected – L.B Aug 11 '14 at 15:06
  • @L.B Isn't `Form_Load` webforms? – dav_i Aug 11 '14 at 15:07
  • What part of *it will run sync* are you not getting? Yes, you can put that code in in, but it won't actually do anything async. It will still hold on to the thread until all work is completed in the method. – Chris Pratt Aug 11 '14 at 15:08
  • @dav_i No, winforms.. Just easier to test what Chris said – L.B Aug 11 '14 at 15:08
  • @ChrisPratt Which part do you not understand? *TEST IT*. It would be faster than writing 2 similar comments.. – L.B Aug 11 '14 at 15:09
  • 2
    Wow, well, there's some breakdown in understanding here. The fact that you decorate a method with async and it runs, does not mean it ran async. I'm totally positive that you got that code to run, but it absolutely did not run asyncronously. Perhaps you don't actually understand what asynchronous means? All it means is that the method will give up its thread to be used by some other process while it's waiting for the operation to complete. Then, once the operation is completed it requests the thread back and continues on. A sync method just hangs onto the thread the whole time. – Chris Pratt Aug 11 '14 at 15:12
  • `private async void Form1_Load(object sender, EventArgs e) { await Task.Delay(10000); MessageBox.Show("10 seceonds elapsed and UI was not blocked"); }` – L.B Aug 11 '14 at 15:14
  • Then that code's not running in the UI thread. Otherwise, it would have blocked. – Chris Pratt Aug 11 '14 at 15:17
  • OK these comments started to repeat themselves. No need to discuss a code I already tested... – L.B Aug 11 '14 at 19:03
  • 1
    You're both right, but the resulting behavior is likely to be very wrong. The filter will run, but the pipeline will continue before the async filter finishes. For example, let's say you wanted to include the result of the external call (or e.g. check it and return some other HTTP response code): this will fail, because the result will already be in progress when the async call returns (that's what async void does, "fire and forget") – Mark Sowul Oct 29 '15 at 19:41
  • I've recently [published a library](https://www.nuget.org/packages/Hydrogen.Extensions.Mvc5.Async) that adds proper support for async filters (heavily based on code in from [ASP.NET MVC Core](https://github.com/aspnet/Mvc)). Source is also available here: https://github.com/jdaigle/Hydrogen.Extensions.Mvc5. – Joseph Daigle Mar 21 '17 at 02:36

2 Answers2

11

Yes, it's still true. Web API 2 has support for async action filters, but MVC 5 still does not. I was just personally frustrated by this not too long ago. For the time being, you will either need to run your async method as sync inside the action filter, or repeat the async code that you would have had in an action filter inside each action that requires it, which you then can run as async.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Can you please expand on what you mean by "repeat the async code in each action that requires it"? – dav_i Aug 11 '14 at 14:54
  • Literally, repeat the code. Whatever your action filter had inside, put that code literally inside the action that needs it, which you will be able to run async. Obviously, that's not ideal, so the best way is to probably just run it sync, but if you absolutely must do it async, there's no other way. – Chris Pratt Aug 11 '14 at 14:58
  • 5
    Ugh. That is indeed not ideal. This is such a shame that this is such a lacking feature. – dav_i Aug 11 '14 at 15:00
  • I agree, and it seems like such an easy thing to add as well. Hopefully vNext will support it, but then, vNext is completely different anyways. – Chris Pratt Aug 11 '14 at 15:06
  • 1
    It is supported (already) in vNext. Async action filters as well as async child actions ("view components" in vNext) are both supported for the new MVC/WebAPI controllers. MVC on the current ASP.NET is too tied into the ASP.NET core (in a synchronous way), so I wouldn't expect this to be fixed until vNext. – Stephen Cleary Aug 11 '14 at 15:14
  • @StephenCleary: Good to know. I had heard rumors, but it hadn't been confirmed to me. – Chris Pratt Aug 11 '14 at 15:15
  • No. What's here is all there is. MVC 5 and previous do not support async action filters. Core does. – Chris Pratt Aug 28 '17 at 17:40
0

Some guy kind of 'back ported' it here

https://github.com/jdaigle/Hydrogen.Extensions.Mvc5

I haven't tried it and can't recommend it but if you're in transition to .NET Core it might be worth considering. Fortunately I managed to remove all async code from my extension - this time.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689