WebAPI Integration Package

Aug 16, 2013 at 7:50 AM
Edited Aug 16, 2013 at 7:51 AM
We could really use nuget package for webapi integration (described here).

Or add extension method "RegisterWebAPIControllers" alongside with "RegisterMvcControllers" in mvc integration package.
Coordinator
Aug 16, 2013 at 10:36 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Coordinator
Aug 16, 2013 at 10:44 AM
I wholeheartedly agree. There really should be a package for that. Not having such package available is a major shortcome.

There are however several reasons why there's no package yet. Web API is very different in nature from all other earlier frameworks that Microsoft released in that it is completely built around a asynchronous programming model. This is something that has to be taken into consideration while developing the package. Although the Per Web Request lifestyle can be used for Web API applications that run in IIS, there is no concept of a web request when running in a self hosted environment.

So the question for me is how to relate instances to a certain context, while there is no HTTP context to hold on to. The thread can't be used, since a single Web API request will be executed on multiple threads. Perhaps the ExecutionContext can be of use, but this is still a vague area to me.
Aug 16, 2013 at 10:52 AM
I see. Well, I was speaking from typical webapi usage - i.e. in asp.net MVC website. I never really worked with self hosted web api so I can't suggest anything about that. But I think most people will use webapi from within asp.net web application (web forms or mvc) so at least a package for that would be nice.

As for self hosted web api - I think the only safe route is the same as with .NET Task<> - use scoped lifestyle. Maybe for that kind of usage there could be overloaded RegisterWebAPIControllers method which accepts custom lifestyle?
Coordinator
Aug 16, 2013 at 11:14 AM
Edited Aug 16, 2013 at 11:18 AM
It's true, most developers run Web API in an IIS environment. And although a NuGet package for this can be easily built, we have to be very careful not breaking anyone's code when the package is updated to work with self hosting as well. I rather wait till I get this right, with the downside of developers having to add custom code to their Web API project for bow, instead of breaking their code in a second release. And of course a scoped lifestyle is the answer, but how should it be implemented? That's the real question. Instances should be cached somewhere and we can't cache them in a thread (lifetime scope) or web request (web request scope). Again, probably using a ExecutionContext, but that's something that needs some research though.
Aug 16, 2013 at 11:15 AM
Yeah, I understand. I don't have experience with self hosting webapi to help you out on this one.
Developer
Aug 30, 2013 at 8:33 AM
As suggested by Steven I have created an ExecutionContextScope impl for testing and attached it to https://simpleinjector.codeplex.com/workitem/20082 (unfortunately I did not take a closer look on the code structure yesterday and did not realize that "SimpleServiceLocator" is the trunk - the execution context scope test inherits copy&paste from SimpleInjectorV2.0 - but for a proof-of-concept that should be enough).

Since it is basically the existing LifetimeScope with some thread-safety locking added I would suggest to use a common base for a ThreadLocal<> based LifetimeScope and a logical call context based ExecutionContextScope. Also possible would be to measure the performance difference between ThreadLocal<> and CallContext.Logical(Get|Set)Data() and then maybe simply always use CallContext in LifetimeScope.

I am not sure if contributions to the project are welcome but I would love to work on a clean patch for the LifetimeScope extension (based on the trunk version :o) ).
Developer
Aug 30, 2013 at 10:23 AM
On my machine CallContext.LogicalGet/SetData is about 6 times slower than using ThreadLocal<>:
100000000x LogicalGet/SetData took: 00:00:17.8293239
100000000x ThreadLocal<> took: 00:00:03.3714636
@Steve: Do you think the perf penalty is negligible and it would be worth to use CallContext in LifetimeScopeManager to support cross-thread scoping for async operations? Since I am personally making quite a lot of use of the async feature I would opt for replacing ThreadLocal<> by CallContext. I guess in most applications LifetimeScoping is used only for a handful objects.

I used the following code to measure the performance (tuple counting to work with some real objects - accounts for about 0.5s for n=100 million):
static void ExecutionCtxVsThreadLocal(int n)
{
    string name = Guid.NewGuid().ToString("N");
    System.Runtime.Remoting.Messaging.CallContext.LogicalSetData(name, Tuple.Create(0));

    var start = DateTime.UtcNow;
    for (int i = 0; i < n; ++i)
    {
        var b = CallContext.LogicalGetData(name) as Tuple<int>;
        CallContext.LogicalSetData(name, Tuple.Create(b.Item1 + 1));
    }
    var end = DateTime.UtcNow;
    Console.WriteLine("{0}x LogicalGet/SetData took: {1}", n, end - start);

    var threadLocal = new ThreadLocal<Tuple<int>>();
    threadLocal.Value = Tuple.Create(0);
    start = DateTime.UtcNow;
    for (int i = 0; i < n; ++i)
    {
        var b = threadLocal.Value;
        threadLocal.Value = Tuple.Create(b.Item1 + 1);
    }
    end = DateTime.UtcNow;
    Console.WriteLine("{0}x ThreadLocal<> took: {1}", n, end - start);
}
Coordinator
Aug 30, 2013 at 10:56 AM
Hi Blueling,

Thanks for taking the time to look into this and coming up with a POC. That will be very useful for building an integration package. I will probably start investiging further after the V2.4 time frame (that's currently being built in the trunk).
did not realize that "SimpleServiceLocator" is the trunk
I'm sorry, you're not the first to trip over the branch naming. It's some unfortunate legacy that I can't change :-(.
I am not sure if contributions to the project are welcome
Contributions are welcome, but unfortunately the project is still under TFS. I plan to migrate to GIT after GIT support for VS has gone RTM.

In the meantime, if you to keep updated about your findings. Perhaps you are using this code in your own Web API project. That would be a nice test.
it would be worth to use CallContext in LifetimeScopeManager
I have to look into this more closely, since any behavioral change of the lifetime scope might break current clients, and that's not something that I like to do.

Thanks again for your time!
Developer
Aug 30, 2013 at 5:05 PM
Hi Steve,

today I have refactored SimpleInjector.Extensions.ExecutionContextScoping to share code with the existing LifetimeScope implementation but at the same time to offer the CallContext.LogicalGet/SetData() based context through a separate ExecutionContextScope class with its own set of container extension methods ... common code is now in TrackingScopeRegistration.cs and TrackingScope.cs

See the file SimpleInjector.Extensions.ExecutionContextScoping.zip which is now attached to: https://simpleinjector.codeplex.com/workitem/20082

I will use this in my current web project and do some active testing over the next few weeks. I will let you know if something breaks. ;-).

Andreas

PS: BTW SimpleInjector is really a fantastic project: Absolutely professionally designed and implemented, well documented and lightning fast! I love it. ;-)
Developer
Sep 2, 2013 at 6:49 AM
Hi Steve,
it would be worth to use CallContext in LifetimeScopeManager
I have to look into this more closely, since any behavioral change of the lifetime scope might break current clients, and that's not something that I like to do.
I see the following main options:

A) Simply switch from TLS to CallContext, risk to break existing code.
B) Offer only one LifetimeScope type but require the developer to explicitely enable execution context scopes through an option or call during container initialization.
C) Offer two different implementations (e.g. as I did it in the SimpleInjector.Extensions.LifeTimeScoping impl wich is attached to work item 20082).

I would go for A because I think chances of breaking existing code by switching from TLS to CallContext are quite low. Without capturing and restoring the execution context CallContext behaves (beside the slight performance degradation) identical to thread local storage. The wrong-thread dispose and the outside-of-scope exceptions both make it very unlikely that existing code tried to use LifetimeScope over thread boundaries. Of course you cannot rule out that any code relies on LifetimeScope using ThreadLocal instead of CallContext (e.g. if inside of a LifetimeScope block somebody is dispatching requests and as part of this restoring their execution context which might break if the execution context would suddenly "carry" the scoped interfaces) but I think simplicity of the library is worth risking this.

Nowadays you expect code which uses logical flow over multiple threads to work and you will definitely hate the Dispose thread-affinity-check exception ;-). The LifetimeScope should support code like:
using (var scope = container.BeginLifetimeScope())
{
    // ...
    await Task.Delay(100);
    // ...
}
Andreas
Coordinator
Sep 2, 2013 at 8:39 PM
So true, everybody hates the thread-affinity-check, including me :-(
Coordinator
Sep 4, 2013 at 8:10 AM
Nicholas Blumhardt (the creator of Autofac) seems to have had the same problems while writing Serilog. He came to the same conclusion (use CallContext.LogicalGetData() under .NET 4.5) and refers to the same article by Stephen Cleary.
Coordinator
Sep 6, 2013 at 12:00 PM
What I'm really interested in to find out, is how the current implementation of Web API passes on the HttpContext.Current from thread to thread. I've browsed through the source code, but I have no idea where to look.
Developer
Sep 6, 2013 at 12:28 PM
Seems to use CallContext.HostContext .. which uses Thread.ExecutionContext. ExecutionContext.Reader to read from the execution context (an internal class). The HostContext property uses executionContextReader.LogicalCallContext.HostContext; internally...

I think for passing the ExecutionContext between threads ExecutionContext.Capture() and .Run() will be used. This is done nearly everywhere in the .net Fx .. e.g. for queueing items to the ThreadPool, IO-Completion Ports, Task, timer and WinRT callbacks. If you run a "used by" analysis of ExecutionContext.Run() you will see what I mean.

This article describes it nicely: http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx
"All of the methods in the .NET Framework that fork asynchronous work capture and restore ExecutionContext in a manner like this (that is, all except for those prefixed with the word “Unsafe,” which are unsafe because they explicitly do not flow ExecutionContext). "
Coordinator
Mar 2, 2014 at 5:53 PM
We just released Simple Injector v2.5 and it now contains an Web API integration package. Please see the Web API integration guide for more information.
Marked as answer by dot_NET_Junkie on 3/2/2014 at 10:53 AM