13

I need to call a method returning a Task from within

public override void OnActionExecuting(ActionExecutingContext filterContext)

It wont let me make this method async it throws the following

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

and when calling

 entityStorage.GetCurrentUser().Result

I get a deadlock. How can I avoid this?

I have been playing around with it coming up with stuff like

entityStorage.GetCurrentUser().Result.ConfigureAwait(false).GetAwaiter().GetResult();

But this isn't working. How can I do it? My solution will need to work with ASP.NET 4 and the Async Targetting Pack, I can't use ASP.NET 4.5 as am deploying to Azure.

Tom
  • 12,591
  • 13
  • 72
  • 112

3 Answers3

13

The cause of the deadlock is explained here. In short, don't block on async code. You should use ConfigureAwait(false) in your library async code and await the results (not use Result or Wait).

Update: Please vote here for the MVC team to add support for async action filters.

Samuel Caillerie
  • 8,259
  • 1
  • 27
  • 33
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • I'm having a hard time find the link/page that says so, but I've had others tell me in the past that if providing a link (especially one external to SO) that you should copy the key bits into the SO answer. It's somewhat mentioned here, though, FWIW: http://meta.stackexchange.com/questions/118582/what-is-an-acceptable-answer – James Manning Aug 09 '12 at 17:55
  • You are correct; the meta question you're referring to was recently deleted as part of the "let's make SO a nicer place" strategy. However, as Jon Skeet commented: if I know the answer and only have time to post a link, it's better than nothing. – Stephen Cleary Aug 10 '12 at 00:10
  • The problem I had is that if I tried to use the async keyword and make the method async then the method threw 'An asynchronous module or handler completed while an asynchronous operation was still pending.' – Tom Aug 16 '12 at 14:00
  • ASP.NET is telling you that you can't use asynchronous methods there. You can kind of force it using `ContinueWith`, but that brings its own problems in ASP.NET. – Stephen Cleary Aug 16 '12 at 16:20
  • OK, so is there a good solution to doing this without using .NET 4.5? The only other solution I found was the one I put as a comment to the question, which I actually ended up using as I needed to store a cookie after the call which wasn't possible when using ContinueWith – Tom Aug 29 '12 at 13:42
  • The core problem is that you aren't *supposed* to do asynchronous processing in `OnActionExecuting`. I think the cleanest way to force it is to use `ConfigureAwait(false)` inside `GetCurrentUser` and then use [`WaitAndUnwrapException`](http://nitoasyncex.codeplex.com/) to block on the `Task` returned by `GetCurrentUser`. – Stephen Cleary Aug 29 '12 at 14:05
2

Since await is just syntax sugar for the compiler rewriting a continuation for you, the most 'direct' path would be to take whatever code was going to follow your await and make it a ContinueWith call.

So, something like:

entityStorage.GetCurrentUser().ContinueWith(t =>
{
    // do your other stuff here
});
James Manning
  • 13,429
  • 2
  • 40
  • 64
1

If you MUST convert asynch to synch.

public User GetCurrentUserSynch()
    {
        return Task.Run(() =>
        {
            var asyncResult = entityStorage.GetCurrentUser();
            while (!asyncResult.IsCompleted)
            {
                Application.Current.TryFindResource(new object()); // This is for WPF but you can do some other nonsense action of your choosing
            }

            return asyncResult.Result;
        }).Result;
    }

Otherwise use @Stephen's answer.

Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265