Search code examples
asp.net-web-apisimple-injector

Using SimpleInjector Is there a way to replicate RegisterWebApiRequest<T>() with .net 4/webapi 1?


Following up on this question. I'm working through example of using SimpleInjector and WebAPI. Unfortunately, where I want to utilize WebAPI KB2568167 and KB2915689 prevent me from upgrading to .net 4.5. So I'm stuck using .net 4.0 & WebAPI v1 (4.0.30506.0) at the moment.

Is there a way to replicate the RegisterWebApiRequest<T>() scoping with the older version of WebAPI?

While I the nu-get packages only contain .net 4.5 versions, I was able to download the code and get a framework 4.0 compile without much trouble. When calling var container = request.GetDependencyScope() in my Message Handler a SimpleInjectorWebApiDependencyResolver class is returned. Attempting to retrieve an instance out of the container like so:

  var poco = (SimplePOCO) container.GetService(typeof(SimplePOCO));

results in the following error::

The registered delegate for type SimplePOCO threw an exception. The SimplePOCO is registered as 'Web API Request' lifestyle, but the instance is requested outside the context of a Web API Request.

Am I just missing something in my config? Is there an alternative -- like creating my own message handler?


UPDATE

After posting the codeplex issue, I went back to basics. I took a plain vanilla Mvc WebApi project, referenced my compiles of SimpleInjector, SimpleInjector.Integration.WebApi, and SimpleInjector.Extensions.ExecutionContextScoping.

Like @blueling I was able to get it working in a message handler.

So what's different? One thought I had is that my non-functioning project is bare bones -- just WebApi and slim web.config. None of the scaffolding and fluff that come with the base project templates are in my solution. Tomorrow I plan to compare the work example to the non-working one reference-by-reference and web.config setting at a time.


UPDATE 2

So a little more debugging, and sure enough Dispose() is being called on the DependencyResolver implementation, but not by me....

Call stack for dispose


Solution

  • I was able to resolve this problem. I'm not entirely clear why dispose was being called on SimpleInjectorWebApiDependencyResolver, but here's what I figured out:

    The BAD Dependency resolver implementation was a copy of the one listed here:

    public sealed class SimpleInjectorWebApiDependencyResolver : IDependencyResolver
    {
      private readonly Container container;
    
      public SimpleInjectorWebApiDependencyResolver(Container container)
      {
        this.container = container;
      }
    
      public IDependencyScope BeginScope()
      {
        return this;
      }
    
      public object GetService(Type serviceType)
      {
        return ((IServiceProvider)this.container).GetService(serviceType);
      }
    
      public IEnumerable<object> GetServices(Type serviceType)
      {
        return this.container.GetAllInstances(serviceType);
     }
    
     public void Dispose()
     {
     }
    }
    

    I noticed there is a bit different copy in the source code I downloaded here.

    public sealed class SimpleInjectorWebApiDependencyResolver : IDependencyResolver
    {
        private readonly Container container;
        private readonly Scope scope;
    
        public SimpleInjectorWebApiDependencyResolver(Container container) : this(container, beginScope: false)
        {
            Requires.IsNotNull(container, "container");
        }
    
        private SimpleInjectorWebApiDependencyResolver(Container container, bool beginScope)
        {
            this.container = container;
    
            if (beginScope)
            {
                this.scope = container.BeginExecutionContextScope();
            }
        }
    
        IDependencyScope IDependencyResolver.BeginScope()
        {
            return new SimpleInjectorWebApiDependencyResolver(this.container, beginScope: true);
        }
    
        object IDependencyScope.GetService(Type serviceType)
        {
            if (!serviceType.IsAbstract && typeof(IHttpController).IsAssignableFrom(serviceType))
            {
                return this.container.GetInstance(serviceType);
            }
    
            return ((IServiceProvider)this.container).GetService(serviceType);
        }
    
        IEnumerable<object> IDependencyScope.GetServices(Type serviceType)
        {
            return this.container.GetAllInstances(serviceType);
        }
    
        void IDisposable.Dispose()
        {
            if (this.scope != null)
            {
                this.scope.Dispose();
            }
        }
    }
    

    After switching over to this version everything worked. I still have potential issues CallContext.LogicalGetData and Nested Execution Contexts, as @Steven was kind enough to point out in the comments. So use this solution at your own risk.