Reputation: 2603
Consider the following ASP.NET Web API Delegating Handler:
public class MyHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var guid = Guid.NewGuid();
HttpContext.Current.Items["foo"] = guid;
// An Async operation
var result = await base.SendAsync(request, cancellationToken);
//All code from this point is not gauranteed to run on the same thread that started the handler
var restoredGuid = (Guid)HttpContext.Current.Items["foo"];
//Is this gauranteed to be true
var areTheSame = guid == restoredGuid;
return result;
}
}
The above example is in a delegating handler, the same problem I am trying to fix applies in Controllers, Business Objects, etc.
I am ultimately trying to provide some simple in-memory shared state between various objects per HTTP Request
As I understand it during Async operations the ASP.NET thread originally running the operation is returned to the thread pool and a different thread may be used to finish the request after the Async operation has completed.
Does this affect the HttpContext.Current.Items
collection?
Is an item that was in the Items
collection guaranteed to be there when the Request resumes?
I'm aware that using HttpContext.Current
is often frowned upon by
the wider community these days for reasons I completely agree
with... I'm just helping someone out of a jam.
Storing this data in the Request.Items
collection is not suitable to solve this problem as my colleague requires a static due to some poor design decisions.
Many Thanks
Upvotes: 24
Views: 6772
Reputation: 457402
As I understand it during Async operations the ASP.NET thread originally running the operation is returned to the thread pool and a different thread may be used to finish the request after the Async operation has completed.
That is correct. But let's talk about async
on ASP.NET for just a minute.
async
requires .NET 4.5. Furthermore, ASP.NET 4.5 introduces a "quirks mode" on the server side, and you have to turn the SynchronizationContext
quirk off. You can do this by either setting httpRuntime.targetFramework
to 4.5
or using an appSettings
with aspnet:UseTaskFriendlySynchronizationContext
value of true
.
If your web.config does not have one of those entries, then the behavior of async
is undefined. See this post for more details. I recommend using the targetFramework
setting and fixing any problems that come up.
Does this affect the HttpContext.Current.Items collection? Is an item that was in the Items collection guaranteed to be there when the Request resumes?
The AspNetSynchronizationContext
preserves the current request context across await
points. This includes HttpContext.Current
(which includes Items
, User
, etc).
Another possibility is CallContext.Logical[Get|Set]Data
, which also flows across await
points. This is useful if you don't want a code dependency on HttpContext
, but has slightly more overhead.
I gave a talk at ThatConference a couple weeks ago on async
on the server side; you may find the slides helpful, particularly the ones dealing with Context and Thread-Local State.
Upvotes: 28
Reputation: 81700
Cutting a long story short, it normally should. Unless you are using ConfigureAwait(false)
which can have a side effect with continuation not flowing the context.
Alternatively try adding this setting in your app.
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
NOTE!! Initially I put false. But it must be true so that context flows.
Upvotes: 6