I am trying to write async code in asp.net 4.8 but and the problem is that HttpContext is null after returning from await. This means that the async code works correctly which is good, but the HttpContext is needed by the original code.
From the comments in below answer by Darin Dimitrov it shows that HttpContext is having this issue since 4.6.1.
Why is HttpContext.Current null after await?
var domains = HttpContext.Current.Cache.Get("domains") as Dictionary<String, Domains>;
if (domains == null)
{
var x = await TrackingMethods.GetTableForCacheAsync().ConfigureAwait(false);
domains = x.domains;
}
/// HttpContext.Current is null here
ConfigureAwait(false) means "don't resume on the captured context". By specifying ConfigureAwait(false), your code is telling the runtime that it doesn't need the ASP.NET request context. But your code does need the ASP.NET request context, since it depends on HttpContext.Current. So using ConfigureAwait(false) here is wrong.
If I don't use ConfigureAwait(false) the code will not run.
This is likely because your code is blocking further up the call stack, causing a deadlock. The ideal solution is to remove the blocking - i.e., use async all the way. Using async all the way is preferable to blocking with ConfigureAwait(false).
However, there are a handful of scenarios where this isn't possible. For example, ASP.NET 4.8 doesn't have proper asynchronous support for MVC action filters or child actions. If you're doing something like this, then you have a couple of options:
Make it synchronous all the way instead of async all the way.
Keep the blocking-over-async-with-ConfigureAwait(false) antipattern but copy out everything your code needs from HttpContext.Current first and pass that data as explicit parameters so the code no longer has a dependency on the ASP.NET request context.
Related
I am migrating my current project to core, and in my current project I have many synchronous method calls from UserManagerExtension class like Create, FindById, AddToRole etc. But despite thorough searching online I am not able to find the same in core.
Is it deprecated in core 2.0? If not, what is the namespace and how to include it?
As far as I can tell, they are gone. However, their existence was suspect even before. All the extensions did was simply run the async versions "synchronously", which basically means they spun off a task that blocked on the async call until it completed. While this technically satisfies having a "synchronous" method, it's actually really bad for web applications as you're sacrificing threads from your pool to do the work synchronously.
In ASP.NET MVC, it was a necessary evil, though, since there were many aspects that did not support async: child actions, action filters, etc. However, in Core, everything is async, and things happening in a web application space should be async. Therefore, there's simply no good reason to ever use sync methods, so that's probably why they no longer exist.
If you still need to run the async methods as sync, for some reason, you can simply block on them yourself:
var user = UserManager.FindByIdAsync(id).GetAwaiter().GetResult();
Be aware, though, that can deadlock in certain situations, because of the context shift. The two ways to avoid that are:
Use ConfigureAwait(false)
var user = UserManager.FindByIdAsync(id).ConfigureAwait(false).GetAwaiter().GetResult();
However, you cannot use ConfigureAwait(false) in something like an action method, since the context must be maintained in order to complete the response. You can use it in a method called by the action method, though:
private ApplicationUser GetUser(string id)
{
return UserManager.FindByIdAsync(id).ConfigureAwait(false).GetAwaiter().GetResult();
}
...
var user = GetUser(id);
Run it in a different thread
Task.Run(() => {
var user = UserManager.FindByIdAsync(id).GetAwaiter().GetResult();
// code here that needs `user`
});
As you can see, with this method the work you send off must be self-contained, so it's probably not a good fit in most scenarios. Also, again, this is extremely bad for web applications.
Long and short, there's not really a good way to run async as sync, and you should avoid it as much as possible. Async should be async all the way. That did use to be an issue in MVC, but now it's not with Core.
I'm looking at how to create a .net web-api method which is asynchronous i.e:
- It runs the actual task as a background task but returns a status straightaway
- It is also passed callback information which it calls back when background task is complete.
I understand the theory and have noticed articles around Request/Acknowledge however im struggling to find a well document .net version of the pattern which achieves the above and doesn't cause multi-threading issues on my web-api? I don't want to do anything bespoke because surely this is a common .net implementation?
ok seems like there nothing magical in terms of solution:
public HttpResponseMessage SomePostMethod(Request request)
{
Validate(request);
QueueForBackGroundAsync(request);
return HttpStatusCode.Accepted;
}
I have a piece very simple code that used to work before I tried upgrading to ASP.NET Identity. This is a real head-scratcher.
Using ASP.NET MVC 5 with ASP.NET Identity 2.1 (latest-and-greatest at the time of writing this post)
I have a "user forgot their email" controller action that does this:
await this.UserManager.SendEmailAsync(
user.Id,
AccountStrings.ForgotPasswordEmailTitle,
string.Format(AccountStrings.ForgotPasswordEmailMessage, callbackUrl));
My user manager uses an implementation of IIdentityMessageService to shoot out the email and my code is in Task SendAsync(IdentityMessage message). Inside that method I need an active Http context since I'm using the Razor view engine to construct the email. Again, this used to work, until today.
As of today, an exception is thrown inside my method as the email is being rendered telling me I have no active Http context. And sure thing, HttpContext.Current is null. It's not null when the SendEmailAsync is called inside the controller action, it's not null after the await in the task continuation, but it's null inside SendEmailAsync.
I'm using the correct <system.web><httpRuntime targetFramework="4.5.1" /></system.web>
flags inside web.config, everything is by the book. And now, after updating some component - whether it's a minor ASP.NET MVC version or ASP.NET Identity version - I don't know - HttpContext.Current is null.
I have an ugly "fix" where I save the HttpContext.Current to a local var as my IdentityMessageService is created and set the current HttpContext to that value while SendAsync is executing, which works, but this definitely smells like a pretty serious regression inside the ASP.NET stack, somewhere.
Has anyone encountered anything similar?
I have not encountered this exact problem, but my gut feeling say that HttpContext.Current should not be used inside SendEmailAsync and this is not a bug, but intentional implementation on the part of Identity framework. I'll guess that a completely new thread is created just to send out an email and this thread does not have access to HttpContext.Current, i.e. this thread does not depend on the Http request/response.
So my thought would be to decouple your email generation from HttpContext. And to help you do this there is a RazorEngine project that allows to work with Razor views, but without HttpContext. This project is available via nuget:
Install-Package RazorEngine
And judging by the documentation it is pretty easy to work with. I have never used it myself though, so can't go beyond what these web-pages are saying.
I have a standard, non-async action like:
[HttpPost]
public JsonResult StartGeneratePdf(int id)
{
PdfGenerator.Current.GenerateAsync(id);
return Json(null);
}
The idea being that I know this PDF generation could take a long time, so I just start the task and return, not caring about the result of the async operation.
In a default ASP.Net MVC 4 app this gives me this nice exception:
System.InvalidOperationException: An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%# Page Async="true" %>.
Which is all kinds of irrelevant to my scenario. Looking into it I can set a flag to false to prevent this Exception:
<appSettings>
<!-- Allows throwaway async operations from MVC Controller actions -->
<add key="aspnet:AllowAsyncDuringSyncStages" value="true" />
</appSettings>
https://stackoverflow.com/a/15230973/176877
http://msdn.microsoft.com/en-us/library/hh975440.aspx
But the question is, is there any harm by kicking off this Async operation and forgetting about it from a synchronous MVC Controller Action? Everything I can find recommends making the Controller Async, but that isn't what I'm looking for - there would be no point since it should always return immediately.
Relax, as Microsoft itself says (http://msdn.microsoft.com/en-us/library/system.web.httpcontext.allowasyncduringsyncstages.aspx):
This behavior is meant as a safety net to let you know early on if
you're writing async code that doesn't fit expected patterns and might
have negative side effects.
Just remember a few simple rules:
Never await inside (async or not) void events (as they return immediately). Some WebForms Page events support simple awaits inside them - but RegisterAsyncTask is still the highly preferred approach.
Don't await on async void methods (as they return immediately).
Don't wait synchronously in the GUI or Request thread (.Wait(), .Result(), .WaitAll(), WaitAny()) on async methods that don't have .ConfigureAwait(false) on root await inside them, or their root Task is not started with .Run(), or don't have the TaskScheduler.Default explicitly specified (as the GUI or Request will thus deadlock).
Use .ConfigureAwait(false) or Task.Run or explicitly specify TaskScheduler.Default for every background process, and in every library method, that does not need to continue on the synchronization context - think of it as the "calling thread", but know that it is not one (and not always on the same one), and may not even exist anymore (if the Request already ended). This alone avoids most common async/await errors, and also increases performance as well.
Microsoft just assumed you forgot to wait on your task...
UPDATE: As Stephen clearly (pun not intended) stated in his answer, there is an inherit but hidden danger with all forms of fire-and-forget when working with application pools, not solely specific to just async/await, but Tasks, ThreadPool, and all other such methods as well - they are not guaranteed to finish once the request ends (app pool may recycle at any time for a number of reasons).
You may care about that or not (if it's not business-critical as in the OP's particular case), but you should always be aware of it.
The InvalidOperationException is not a warning. AllowAsyncDuringSyncStages is a dangerous setting and one that I would personally never use.
The correct solution is to store the request to a persistent queue (e.g., an Azure queue) and have a separate application (e.g., an Azure worker role) processing that queue. This is much more work, but it is the correct way to do it. I mean "correct" in the sense that IIS/ASP.NET recycling your application won't mess up your processing.
If you absolutely want to keep your processing in-memory (and, as a corollary, you're OK with occasionally "losing" reqeusts), then at least register the work with ASP.NET. I have source code on my blog that you can drop in your solution to do this. But please don't just grab the code; please read the entire post so it's clear why this is still not the best solution. :)
The answer turns out to be a bit more complicated:
If what you're doing, as in my example, is just setting up a long-running async task and returning, you don't need to do more than what I stated in my question.
But, there is a risk: If someone expanded this Action later where it made sense for the Action to be async, then the fire and forget async method inside it is going to randomly succeed or fail. It goes like this:
The fire and forget method finishes.
Because it was fired from inside an async Task, it will attempt to rejoin that Task's context ("marshal") as it returns.
If the async Controller Action has completed and the Controller instance has since been garbage collected, that Task context will now be null.
Whether it is in fact null will vary, because of the above timings - sometimes it is, sometimes it isn't. That means a developer can test and find everything working correctly, push to Production, and it explodes. Worse, the error this causes is:
A NullReferenceException - very vague.
Thrown inside .Net Framework code you can't even step into inside of Visual Studio - usually System.Web.dll.
Not captured by any try/catch because the part of the Task Parallel Library that lets you marshal back into existing try/catch contexts is the part that's failing.
So, you'll get a mystery error where things just don't occur - Exceptions are being thrown but you're likely not privy to them. Not good.
The clean way to prevent this is:
[HttpPost]
public JsonResult StartGeneratePdf(int id)
{
#pragma warning disable 4014 // Fire and forget.
Task.Run(async () =>
{
await PdfGenerator.Current.GenerateAsync(id);
}).ConfigureAwait(false);
return Json(null);
}
So, here we have a synchronous Controller with no issues - but to ensure it still won't even if we change it to async later, we explicitly start a new Task via Run, which by default puts the Task on the main ThreadPool. If we awaited it, it would attempt to tie it back to this context, which we don't want - so we don't await it, and that gets us a nuisance warning. We disable the warning with the pragma warning disable.
I have a web application the relies heavily on web services. Everything with the services is done asynchronously and with AddOnPreRequestHandlerExecuteAsync. Anyhow, most of my calls work just fine, but some are returning from their asynchronous service calls to find a null HttpContext.Current.Response/Request object in the endprerequest, which of course errors the instant I try to use either. Both objects (Response and Request are available/not null on beginprerequest of failing calls and work in the endprerequest of other calls).
Anyone run into similar, or have a guess as to what might be the problem?
Update: Seem to have found a solution, if I create a variable for the HttpApplication on Init(of the HttpModule this all occurs in) the HttpContext can be accessed from that variable.
Update: Passing either HttpApplication or HttpContext.Current on the begin function has the same issue. When passed as part of the "State" of the asynchronous call, they end up null in the end function, even though they are valid in the begin function.
Update: I've added some logging and found that the Asynchronous call I am making is returning correctly, the results are there, the callback function is invoked properly.
I suspect I know the problem you're running into. The answer, almost certainly, is to replace usage of HttpWebRequest with WebClient, and to use the *Async methods of WebClient.
Here's the long explanation: there are two totally different Async programming models: the IAsyncResult Async Pattern and the Event-based Asynchronous Pattern. The IAsyncResult pattern uses BeginXXX and EndXXX methods, uses IAsyncResult instances, uses delegates for callbacks, and supports waiting for completion. The Event-based pattern uses XXXAsync methods to initiate async actions, uses XXXCompleted events instead of callbacks to handle completion, and (this is important to your case) transfers thread-specific context into every callback event handler.
In other words, if you put your callback code inside a XXXCompleted event handler (like WebClient.DownloadStringCompleted), then HttpContext.Current will be populated correctly.
If, however, you use a BeginXXX method (like HttpWebRequest.BeginGetResponse) and a delegate callback, your callback will be executed in the context of a thread that does not guarantee to have the right ASP.NET context attached.
Generally, .NET Framework library classes either use one async pattern or the other. Typically, the lower-level classes (e.g. HttpWebRequest) will use the IAsyncResult pattern, while the higher-level classes (e.g. WebClient) will use the event-based pattern. Some oddball classes (e.g. auto-generated .NET Remoting proxies) will support both patterns, but that's a rarity.
So if it's easy to do, I'd suggest moving to WebClient and event handlers instead of HttpWebRequest and callback delegates. This should solve your problem. If switching to WebClient is too hard, comment and I can probably suggest some more obscure alternatives.
Seem to have found a solution, if I create a variable for the HttpApplication on Init(of the HttpModule this all occurs in) the HttpContext can be accessed from that variable.