Shouldn't the Simple Injector Web API DependencyResolver create a LifetimeScope?

Coordinator
Aug 31, 2012 at 10:36 AM

This question was originaly asked by kimsagro and was migrated from this thread.

Shouldn't the DependencyResolver (as shown here) create an inner scope as WebAPI will use BeginScope to get a scope instance that it can use to resolve the controller from and will then dispose it at the end of the request, so that anything registered as lifescope with be dispose as well

public class SimpleInjectorDependencyResolver : IDependencyResolver
{
    private readonly Container _container;
    private readonly LifetimeScope _lifetimeScope;

    public SimpleInjectorDependencyResolver(Container container)
        : this(container, false)
    {
    }

    private SimpleInjectorDependencyResolver(Container container, bool createScope)
    {
        if (container == null) throw new ArgumentNullException("container");

        _container = container;
        
        if (createScope)
        {
            _lifetimeScope = _container.BeginLifetimeScope();
        }
    }

    public void Dispose()
    {
       if (_lifetimeScope != null)
       {
           _lifetimeScope.Dispose();
       }
    }

    public object GetService(Type serviceType)
    {
        return ((IServiceProvider) _container).GetService(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.GetAllInstances(serviceType);
    }

    public IDependencyScope BeginScope()
    {
        return new SimpleInjectorDependencyResolver(_container, true);
    }
}
Coordinator
Aug 31, 2012 at 10:36 AM

Using lifetime scoping inside an web request of ASP.NET Web API might not be a good idea. A lifetime scope will only work correctly if the scope is created and disposed on the same thread, and the complete operation within that scope is executed on that thread. I'm not sure how Web API handles this, but I know that both ASP.NET Web Forms (example) and ASP.NET MVC (example) allow requests to finish on a different (request) thread. If Web API allows this (which I'm not sure, but this is pretty likely, since there's an ExecuteAsync method), the behavior of the Lifetime Scope will not be deterministic.

When you look closely at how Web API uses IDependencyResolver.BeginScope(), you'll see that it calls BeginScope at the beginning of a web request, and disposes this scope at the end of that request. In other words, a (default) scope has the lifetime of a single request. Simple Injector has a lifetime for this with equal behavior: Per Web Request.

To be able to use the Per Web Request lifetime, you don't have to do anything special in your IDependencyResolver implementation (so returning this from BeginScope() will do just fine). You can just register instances using RegisterPerWebRequest and you're done (instances registered using these extension methods will automatically be disposed when the request ends).

This doesn't mean that you can't use lifetime scoping at all. For scopes that have a lifetime shorter than one web request or scopes that run in a background thread (where HttpContext.Current == null), a lifetime scope would be a good fit.

Coordinator
Aug 31, 2012 at 10:37 AM
Edited Sep 5, 2012 at 3:29 PM

[This response was originaly posted by kimsagro and was migrated from this thread.]

I assume that the Per Web Request lifetime relies on HttpContext.Current which is an issue as I'm using webapi in self hosted mode which means that HttpContext.Current is always null (see http://stackoverflow.com/questions/11347807/httpselfhostserver-and-httpcontext-current).

So how can I achieve the equivalent of 'Per Request' lifetime in this scenario given that both Lifetime Scoping and Per Web Request lifetime are out of the question ?

Coordinator
Aug 31, 2012 at 10:37 AM
Edited Sep 1, 2012 at 4:48 PM

Look at that. It starts making sense now why the Web API IDependencyResolver contains a BeginScope method. While it doesn't make much sense in a web environment, it does make much more sense in a WCF invironment, where there is no HttpContext.

Using a Lifetime Scope is safe if everything inside that scope runs on the same thread. MVC will only run a controller over multiple threads when you explicitly implemented *Async and *Completed action methods. This will probably also be the case with Web API, but you'll have to check, since I'm not sure about this.

To guard yourself from any problems, you can add a check to your dependency resolver. Just change your Dispose method to the following:

public void Dispose()
{
   if (_lifetimeScope != null)
   {
        _lifetimeScope.Dispose();
        if (Environment.CurrentManagedThreadId != _initialThreadId)
        {
            throw new InvalidOperationException(string.Format(
                "The scope ended on a different thread than where it was started. " +
                "A LifetimeScope does not support being used over multiple threads. " +
                "Initial thread id: {0}. Current thread id: {1}.",
                _initialThreadId, Environment.CurrentManagedThreadId));
        }
    }
}

And add an private _initialThreadId field to the SimpleInjectorDependencyResolver and initialize it inside your constructor as follows:

_initialThreadId = Environment.CurrentManagedThreadId;

By the way, the next release of the SimpleInjector.Extensions.LifetimeScoping.dll (and NuGet package) will contain a breaking change. Currently, when instances that are configured as Per Lifetime Scope, but are requested outside the context of a lifetime scope, are resolved as singletons. This means that the container will return that same instance when there is no lifetime scope. This however, can lead to hard to track bugs. Especially since new features in the .NET framework make it easier to do multi-threaded programming. For instance, it is easy to miss that calling container.GetInstance<> inside a Task, runs on background thread and would resolve that instance as singleton. Instead, the next version will throw an exception when resolving such instance outside the context of a lifetime scope, since this is almost certainly an error. If a singleton is really what you need in that case, there are ways to configure that. Hopefully, this will give you more certainty about the correctness of your configuration (since it will fail fast, instead of silently returning the wrong thing). The next version will also throw an exception when a lifetime scope is disposed on a different thread than that it is created. So the manual check in the Dispose method of the IDependencyResolver as shown above will be redundant in the next version.

Marked as answer by dot_NET_Junkie on 11/4/2013 at 2:02 AM