8

ASP.NET is known to exhibit what is called "thread agility". In short, it means that multiple threads may be employed to fulfill a single request, although not more than one thread at a time. This is an optimization that means a thread waiting for asynchronous I/O may be returned to the pool and used to service other requests.

However, ASP.NET does not migrate all thread-related data when moving a request. Microsoft either forgot to do so, or thought that using thread-local storage (made easy by the ThreadStatic attribute) was something only the people coding ASP.NET themselves should do.

Based on quick googling, it seems to me that the only way to avoid the issue is to rely on HttpContext instead. The context is indeed migrated if ASP.NET decides to switch threads mid-request, so this overcomes the problem. But it creates a brand new headache instead: It ties your application logic to HttpContext, and therefore to a web context. That's not acceptable in all situations (in fact, I'd say it's unacceptable in most). Besides, since HttpContext is sealed and has internal constructors, you cannot mock or stub it, and therefore your logic also becomes untestable.

According to this (old) blog post, CallContext does NOT work, which is pretty infuriating given that a call context is conceptually precisely a logical thread!

Is there a simple way to reliably implement "per-LOGICAL-thread" isolation that will work in asp.net contexts as well as other contexts?

If not, does anyone know of a lightweight third-party framework that solves the problem? Does StructureMap behave correctly when ASP.NET migrates threads?

I would like a general answer, but in case anyone wonders, the specific use case I'm looking at is for use of Entity Framework in a SharePoint context. We're unfortunately stuck with SP-2010 and EF 3.5 for a while. EF basically requires that data is saved using the same context as they were originally read from - or else you have to keep track of changes yourself. I would like to introduce a "current model" concept. The first time the model is called upon in processing each HTTP request it should be instantiated, and then that same model instance should be used for the duration of the request. But the code relying on "Model.Current" should also work if executed in the context of a timer job. I'm fine with the timer job code explicitly disposing of the model when done with it (a task I'd like to give to a handler for HttpApplication.EndRequest in the SharePoint web context).

There may be reasons not to do this, and that's interesting too, but I would anyway really appreciate to learn of a way to achieve "logical thread isolation" in an asp.net context, as it'd be remarkably useful.

The Dag
  • 1,811
  • 16
  • 22
  • You are talking about async handlers in asp.net, right? Because by default asp.net is **not** asynchronous, thus every request is handled by a single blocking I/O thread. In case of async handlers, you can declare your own `IWhateverContext` interface, and wrap HttpContext by an implementing class, if you want to decouple your logic from HttpContext. – shay__ Oct 26 '15 at 12:48
  • No, I'm talking about any plain-vanilla asp.net code. A lot of .NET framework code uses async I/O under the hood (which is great), and requests being migrated across to new threads can happen in any regular asp.net app (which *would* be ok, if ALL thread aspects were correctly migrated so it was transparent and didn't randomly break stuff). – The Dag Oct 27 '15 at 11:59
  • Btw, how does *wrapping* HttpContext decouple from it? The dependency would still be there, and it would crash whenever there is no current HttpContext - such as in a timer job, if you used the same business layer for a mobile app, windows forms or WPF app, et cetera. – The Dag Oct 27 '15 at 12:00
  • I thought you were asking to decouple it for testing purposes - the wrappers could be easily mocked. But I'm more curious about your claim that HttpContext can float between threads even when not using TPL or async features - can you give an example? I'm not an asp.net expert but I've never encountered such a behavior. – shay__ Oct 27 '15 at 12:45
  • I gave a link in the post. And I've experienced it breaking EntLib data access block (4 - version 5 cured it somehow) when a system was under very heavy load (more than one million concurrent users). – The Dag Oct 27 '15 at 14:34
  • Jon Skeet seems to be a "believer" as well - see his answer in http://stackoverflow.com/questions/657735/how-is-asp-net-multithreaded – The Dag Oct 27 '15 at 14:38
  • Thanks, it was an interesting reading, but none of his references give an evidence of thread-switching while **not** using TPL or async/await features. It does say that the request switches threads before and after IIS hands it to your application, but not **during** your application code. I will keep up with this post, until someone gives an answer to your interesting question :) – shay__ Oct 27 '15 at 15:47
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/93523/discussion-between-the-dag-and-shay). – The Dag Oct 27 '15 at 16:29

1 Answers1

0

There is a nice post related to the problem: Implicit Async Context ("AsyncLocal").

If I got everything right, Logical CallContext i.e. CallContext.LogicalGetData and CallContext.LogicalSetData make it real to migrate immutable data correctly given you live in the world past .NET 4.5. This immutable limitation is a nut but still...way to go.

Zverev Evgeniy
  • 3,643
  • 25
  • 42