Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Microsoft made the decision to avoid as much backwards-compatibility issues as possible when bringing <code>async</code> into ASP.NET. And they wanted to bring it to all of their "one ASP.NET" - so <code>async</code> support for WinForms, MVC, WebAPI, SignalR, etc.</p> <p>Historically, ASP.NET has supported clean asynchronous operations since .NET 2.0 via the Event-based Asynchronous Pattern (EAP), in which asynchronous components notify the <code>SynchronizationContext</code> of their starting and completing. .NET 4.5 brings the first fairly hefty changes to this support, updating the core ASP.NET asynchronous types to better enable the Task-based Asynchronous Pattern (TAP, i.e., <code>async</code>).</p> <p>In the meantime, each different framework (WebForms, MVC, etc) all developed their own way to interact with that core, keeping <em>backwards compatibility</em> a priority. In an attempt to assist developers, the core ASP.NET <code>SynchronizationContext</code> was enhanced with the exception you're seeing; it will catch many usage mistakes.</p> <p>In the WebForms world, they have <code>RegisterAsyncTask</code> but a lot of people just use <code>async void</code> event handlers instead. So the ASP.NET <code>SynchronizationContext</code> will allow <code>async void</code> at appropriate times during the page lifecycle, and if you use it at an inappropriate time it will raise that exception.</p> <p>In the MVC/WebAPI/SignalR world, the frameworks are more structured as services. So they were able to adopt <code>async Task</code> in a very natural fashion, and the framework only has to deal with the returned <code>Task</code> - a very clean abstraction. As a side note, you don't need <code>AsyncController</code> anymore; MVC knows it's asynchronous just because it returns a <code>Task</code>.</p> <p>However, if you try to return a <code>Task</code> <em>and</em> use <code>async void</code>, that's not supported. And there's little reason to support it; it would be quite complex just to support users that aren't supposed to be doing that anyway. Remember that <code>async void</code> notifies the core ASP.NET <code>SynchronizationContext</code> directly, bypassing the MVC framework completely. The MVC framework understands how to wait for your <code>Task</code> but it doesn't even know about the <code>async void</code>, so it returns completion to the ASP.NET core which sees that it's not <em>actually</em> complete.</p> <p>This can cause problems in two scenarios:</p> <ol> <li>You're trying to use some library or whatnot that uses <code>async void</code>. Sorry, but the plain fact is that the library is broken, and will have to be fixed.</li> <li>You're wrapping an EAP component into a <code>Task</code> and properly using <code>await</code>. This can cause problems because the EAP component interacts with <code>SynchronizationContext</code> directly. In this case, the best solution is to modify the type so it supports TAP naturally or replace it with a TAP type (e.g., <code>HttpClient</code> instead of <code>WebClient</code>). Failing that, you can use TAP-over-APM instead of TAP-over-EAP. If neither of those are feasible, you can just use <code>Task.Run</code> around your TAP-over-EAP wrapper.</li> </ol> <hr> <p>Regarding "fire and forget":</p> <p>I personally never use this phrase for <code>async void</code> methods. For one thing, the error handling semantics most certainly do not fit in with the phrase "fire and forget"; I half-jokingly refer to <code>async void</code> methods as "fire and crash". A true <code>async</code> "fire and forget" method would be an <code>async Task</code> method where you ignore the returned <code>Task</code> rather than waiting for it.</p> <p>That said, in ASP.NET you almost never want to return early from requests (which is what "fire and forget" implies). This answer is already too long, but I have a <a href="http://blog.stephencleary.com/2012/12/returning-early-from-aspnet-requests.html">description of the problems on my blog, along with some code to support ASP.NET "fire and forget"</a> if it's truly necessary.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload