Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any technical reasons Simple Injector cannot support Web API on .NET 4.0?

This is really a follow up on this question. One of the Simple Injector developers pointed out a useful tidbit about Simple Injector, and I thought it would be good to make it more accessible.

So, are there any technical hurdles to supporting Simple Injector with Web API 1 and .NET 4.0? The source code is easy enough to download and compile back. It seems to work just fine.

like image 297
EBarr Avatar asked Mar 13 '14 22:03

EBarr


2 Answers

We deliberately chose to NOT support .NET 4.0 for Web API, because the WebApiRequestLifestyle makes use of CallContext.LogicalGetData which behaves different under .NET 4.0. This behavior is so significantly different that it can cause bugs when using nested ExecutionContextScope instances in background threads and Tasks that run in parallel.

What changed in this respect is that in .NET 4.5 the Logical Call Context exhibits copy-on-write behavior, which means that when the Logical Call Context is changed from within a parallel operation, it has no impact on the original operation that has spawn this parallel operation. In .NET 4.0, any change to the Logical Call Context from within a parallel operation will be observable from within the main operation.

We decided to depend on this copy-on-write behavior of .NET 4.5, since it allows to have multiple parallel operations been spawned from the main operation which could all start their own ExecutionContextScope (which is what the Web API integration package uses in the background) to run in isolation. This allows them to have their own child scope and when the scope is disposed they run again in the scope of the main operation. Starting in a new scope in a background thread is typically what you would like to do, since your components would otherwise be accessed in parallel, while they might not be thread-safe at all. Starting a new scope ensures that when a new object graph is resolved from the container, new instances are created if they are registered as Per Web API Request or as Per Execution Scope.

Without this copy-of-write behavior, parallel operations would interfere with each other and would see the scopes of other parallel operations, which causes those scopes to get nested, while they in practice will not nest, but overlap. This will cause all sorts of problems such as resulting in falling back to the scope of another parallel operation (instead of falling back on the main operation's scope). This is of course incorrect behavior.

As long as you only start new ExecutionContextScope's in the asynchronous flow (or don't start new scopes at all) instead of starting new scopes in parallel operations, the problem does not exist and your code would run the same under .NET 4.0 and .NET 4.5.

But since this behavior is so fundamentally different and could cause all kinds of problems, or when it seems to work, might again break when you switch to .NET 4.5, we decided that it would be wise to don't try to support .NET 4.0 at all. This prevents developers from falling into this trap, and it makes our own work much easier, since we don't have to document this fundamental difference and it saves us a lot of support time as well, since no matter how good the documentation is, developers will try to use it under .NET 4.0 anyway and this will possibly trigger many new questions on both Stackoverflow and the Simple Injector forum, which we will have to answer.

like image 132
Steven Avatar answered Nov 14 '22 23:11

Steven


This didn't seem quite like a comment, but it's not quite an answer either -- more of a side car.

I wanted to add to @Steven's great explanation. CallContext.LogicalGetData() is one of those little gems inside the .NET Framework. Unfortunately, it's behavior has changed over time, and there are definitely, ahem, subtleties. There's also a dearth of information on the topic. So here are some resources:

  • Mr. CLR himself -- Jeffrey Richter on the topic : http://www.wintellect.com/blogs/jeffreyr/logical-call-context-flowing-data-across-threads-appdomains-and-processes
  • SO's own Stephen Cleary - http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html
  • Another S. Cleary post explaining the behavior in relation to asp.net - Using ASP.NET Web API, my ExecutionContext isn't flowing in async actions
  • CallContext.LogicalGetData Vs. CallContext.GetData
like image 27
EBarr Avatar answered Nov 14 '22 21:11

EBarr