Configuring dependency injection with ASP.NET Web API 2.1
Asked Answered
C

2

29

I'm creating an ASP.NET Web API 2.1 site and as I want to inject dependencies directly into the controllers, I've created my own implementation of IDependencyResolver so that StructureMap will handle that for me.

public class StructureMapDependencyResolver : IDependencyResolver
{
    public IDependencyScope BeginScope()
    {
        return this;
    }

    public object GetService(Type serviceType)
    {
        return ObjectFactory.GetInstance(serviceType);
    }

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

    public void Dispose()
    {
    }
}

I've then told Web API to use this class by adding this line to the Application_Start method in Global.asax

GlobalConfiguration.Configuration.DependencyResolver = new StructureMapDependencyResolver();

That compiled but when I tried to access any of the API methods in a browser I got an error like this

No Default Instance defined for PluginFamily System.Web.Http.Hosting.IHostBufferPolicySelector, System.Web.Http

That one was relatively easy to solve as I added a line to my StructureMap configuration

this.For<IHostBufferPolicySelector>().Use<WebHostBufferPolicySelector>();

However then I got other similar errors for other System.Web.Http classes and while I could resolve some of them I am stuck on how to deal with 3 of them, namely ITraceManager, IExceptionHandler and IContentNegotiator.

The issue is that TraceManager which seems to be the default implementation of ITraceManager is an internal class and so I can't reference it in my StructureMap configuration.

So am I going about this completely the wrong way or is there some other way to inject these internal classes?

Czarevna answered 20/2, 2014 at 21:15 Comment(0)
A
50

I'd like to give you a suggestion and explanation why not to go this way, and how to do it differently (I'd even say better and properly).

The full and complete explanation of the inappropriate IDependencyResolver design could be found here: Dependency Injection and Lifetime Management with ASP.NET Web API by Mark Seemann

Let me cite these essential parts:

The problem with IDependencyResolver

The main problem with IDependencyResolver is that it's essentially a Service Locator. There are many problems with the Service Locator anti-pattern, but most of them I've already described elsewhere on this blog (and in my book). One disadvantage of Service Locator that I haven't yet written so much about is that within each call to GetService there's no context at all. This is a general problem with the Service Locator anti-pattern, not just with IDependencyResolver.

And also:

...dependency graph need to know something about the context. What was the request URL? What was the base address (host name etc.) requested? How can you share dependency instances within a single request? To answer such questions, you must know about the context, and IDependencyResolver doesn't provide this information.

In short, IDependencyResolver isn't the right hook to compose dependency graphs. **Fortunately, the ASP.NET Web API has a better extensibility point for this purpose. **

ServiceActivator

So, the answer in this scenario would be the ServiceActivator. Please take a look at this answer:

An example of the ServiceActivator:

public class ServiceActivator : IHttpControllerActivator
{
    public ServiceActivator(HttpConfiguration configuration) {}    

    public IHttpController Create(HttpRequestMessage request
        , HttpControllerDescriptor controllerDescriptor, Type controllerType)
    {
        var controller = ObjectFactory.GetInstance(controllerType) as IHttpController;
        return controller;
    }
}

All we can do with StructureMap, is in place. The key features of the Web API framework are still in place... we do not have to hack them. And we are also rather using DI/IoC then Service locator

Alexina answered 27/2, 2014 at 4:18 Comment(5)
Thank you for such a detailed answer, I will be attempting to implement your suggestion in the next few days.Czarevna
Good luck with StructureMap, Amazing tool ;)Broadcasting
Thanks for the explanation Radim. I do have couple of questions. 1. Use of ObjectFactory is deprecated right? Shouldn't we be using container.GetInstance(type) where container is an instance of IContainer? Please see this gist.github.com/saip106/4e93c617a399c17ce304 2. Why are we taking in HttpConfiguration as a constructor parameter when we are not using it? 3. How are we solving the problem of not having the context in scope?Laforge
The most reasonable would be to ask brand new question...this is out of scope of comments, I'd say.Broadcasting
Ahem, is not ObjectFactory.GetInstance a Service Locator?Eyla
A
-3

Just try using UnityHierarchicalDependencyResolver instead of the other one. It worked for me. This is for future reference if somebody would like to use Unity

Aestival answered 24/2, 2015 at 12:1 Comment(1)
Not relevant as it's clearly stated in the message body AND the tags of the question that this is a StructureMap question.Monterrey

© 2022 - 2024 — McMap. All rights reserved.