4

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

Steven
  • 166,672
  • 24
  • 332
  • 435
EBarr
  • 11,826
  • 7
  • 63
  • 85
  • Did you register the `SimpleInjectorWebApiDependencyResolver`? – Steven Mar 11 '14 at 14:55
  • It is registered. In my MessageHandler inspecting the following call `var container = request.GetDependencyScope();` confirms the type. – EBarr Mar 11 '14 at 21:50
  • I'm able to produce a similar error if I use `WebApiRequestLifestyle` and `SimpleInjectorWebApiDependencyResolver` (by updated from `WebRequestLifestyle` and `SimpleInjectorHttpDependencyResolver`) - in my case the instance *is* being requested outside of a request, specifically in `Global.asax` initialisation `config.MessageHandlers.Add(container.GetInstance());` – qujck Mar 11 '14 at 22:02
  • @EBarr would you mind raising this as an issue for us on the codeplex site? https://simpleinjector.codeplex.com – qujck Mar 11 '14 at 22:06
  • 1
    Done. Happy to help test/debug/track down. – EBarr Mar 11 '14 at 22:29
  • I am not able to reproduce the issue: I have recompiled SimpleInjector, SimpleInjector.Extensions.ExecutionContextScoping and SimpleInjector.Integration.WebApi for .net 4.0 and added a reference to https://www.nuget.org/packages/AspNetWebApi/ in a MVC3 project. I am able to resolve instances with request.GetDependencyScope().GetService(typeof(xyz)) inside a MessageHandler. – blueling Mar 11 '14 at 22:37
  • @EBarr: Could it be you are calling Dispose() on the request's dependency scope somewhere else in your code? – blueling Mar 11 '14 at 23:08
  • 1
    Do note that we deliberately chose to *NOT* support .NET 4.0 for Web API, because the `WebApiRequestLifestyle` makes use of [CallContext.LogicalGetData](http://msdn.microsoft.com/en-us/library/system.runtime.remoting.messaging.callcontext.logicalgetdata%28v=vs.110%29.aspx) 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. To prevent developers from falling into this trap we decided to only support .NET 4.5. – Steven Mar 12 '14 at 21:00
  • Not at all. Go for it. – Steven Mar 13 '14 at 21:59

1 Answers1

3

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.

EBarr
  • 11,826
  • 7
  • 63
  • 85
  • 1
    The [SimpleInjectorWebApiDependencyResolver implementation](https://simpleinjector.codeplex.com/wikipage?title=Web%20API%20Integration&version=8) in the documentation expects the user to work with the `WebRequestLifestyle`, while the [Web API integration package](https://www.nuget.org/packages/SimpleInjector.Integration.WebApi/) starts a new `ExecutionContextScope` in the background and users are expected to use the new `WebApiRequestLifestyle` instead. – Steven Mar 14 '14 at 13:19
  • What NuGet package version are you using? Or is it your custom build? I need this temporary fix too, so if you have a working fork, it'd be a great help! – Mrchief Apr 07 '14 at 17:51