84

Has anyone had any success running using an IoC container to inject dependencies into ASP.NET WebAPI controllers? I cannot seem to get it to work.

This is what I'm doing now.

In my global.ascx.cs:

    public static void RegisterRoutes(RouteCollection routes)
    {
            // code intentionally omitted 
    }

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        IUnityContainer container = BuildUnityContainer();

        System.Web.Http.GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
            t =>
            {
                try
                {
                    return container.Resolve(t);
                }
                catch (ResolutionFailedException)
                {
                    return null;
                }
            },
            t =>
            {
                try
                {
                    return container.ResolveAll(t);
                }
                catch (ResolutionFailedException)
                {
                    return new System.Collections.Generic.List<object>();
                }
            });

        System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container)); 

        BundleTable.Bundles.RegisterTemplateBundles();
    }

    private static IUnityContainer BuildUnityContainer()
    {
        var container = new UnityContainer().LoadConfiguration();

        return container;
    }

My controller factory:

public class UnityControllerFactory : DefaultControllerFactory
            {
                private IUnityContainer _container;

                public UnityControllerFactory(IUnityContainer container)
                {
                    _container = container;
                }

                public override IController CreateController(System.Web.Routing.RequestContext requestContext,
                                                    string controllerName)
                {
                    Type controllerType = base.GetControllerType(requestContext, controllerName);

                    return (IController)_container.Resolve(controllerType);
                }
            }

It never seems to look in my unity file to resolve dependencies, and I get an error like:

An error occurred when trying to create a controller of type 'PersonalShopper.Services.WebApi.Controllers.ShoppingListController'. Make sure that the controller has a parameterless public constructor.

at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpControllerContext controllerContext, Type controllerType) at System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateInstance(HttpControllerContext controllerContext, HttpControllerDescriptor controllerDescriptor) at System.Web.Http.Dispatcher.DefaultHttpControllerFactory.CreateController(HttpControllerContext controllerContext, String controllerName) at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal(HttpRequestMessage request, CancellationToken cancellationToken) at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

Controller looks like:

public class ShoppingListController : System.Web.Http.ApiController
    {
        private Repositories.IProductListRepository _ProductListRepository;


        public ShoppingListController(Repositories.IUserRepository userRepository,
            Repositories.IProductListRepository productListRepository)
        {
            _ProductListRepository = productListRepository;
        }
}

My unity file looks like:

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
  <container>
    <register type="PersonalShopper.Repositories.IProductListRepository, PersonalShopper.Repositories" mapTo="PersonalShopper.Implementations.MongoRepositories.ProductListRepository, PersonalShopper.Implementations" />
  </container>
</unity>

Note that I don't have a registration for the controller itself because in previous versions of mvc the controller factory would figure out that the dependencies needed to be resolved.

It seems like my controller factory is never being called.

Jim Aho
  • 9,932
  • 15
  • 56
  • 87
Oved D
  • 7,132
  • 10
  • 47
  • 69

11 Answers11

44

Figured it out.

For ApiControllers, MVC 4 uses a System.Web.Http.Dispatcher.IHttpControllerFactory and System.Web.Http.Dispatcher.IHttpControllerActivator to create the controllers. If there is no static method to register what the implementation of these they are; when they are resolved, the mvc framework looks for the implementations in the dependency resolver, and if they are not found, uses the default implementations.

I got unity resolution of controller dependencies working by doing the following:

Created a UnityHttpControllerActivator:

public class UnityHttpControllerActivator : IHttpControllerActivator
{
    private IUnityContainer _container;

    public UnityHttpControllerActivator(IUnityContainer container)
    {
        _container = container;
    }

    public IHttpController Create(HttpControllerContext controllerContext, Type controllerType)
    {
        return (IHttpController)_container.Resolve(controllerType);
    }
}

Registered that controller activator as the implementation in the unity container itself:

protected void Application_Start()
{
    // code intentionally omitted

    IUnityContainer container = BuildUnityContainer();
    container.RegisterInstance<IHttpControllerActivator>(new UnityHttpControllerActivator(container));

    ServiceResolver.SetResolver(t =>
       {
         // rest of code is the same as in question above, and is omitted.
       });
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
Oved D
  • 7,132
  • 10
  • 47
  • 69
38

There is a better solution that works correctly here

http://www.asp.net/web-api/overview/extensibility/using-the-web-api-dependency-resolver

CodeKata
  • 572
  • 4
  • 8
  • 1
    This is a much better solution with MVC RC. You dont need to provide an implementation of IHttpControllerActivator. You implement System.Web.Http.Dependencies.IDependencyResolver instead. – Matt Randle Jun 29 '12 at 15:24
  • 22
    Not a big fan of linking to blogs - could you summarize here and put the link? :) – Nate-Wilkins Apr 12 '14 at 00:22
  • 3
    It looks like a good approach but i am getting several errors like following. Looks like the new resolver cannot resolve several types. Resolution of the dependency failed, type = "System.Web.Http.Hosting.IHostBufferPolicySelector", name = "(none)". Exception occurred while: while resolving. Exception is: InvalidOperationException - The type IHostBufferPolicySelector does not have an accessible constructor. --- At the time of the exception, the container was: Resolving System.Web.Http.Hosting.IHostBufferPolicySelector,( – ATHER Jan 15 '15 at 23:01
  • Microsoft have a Nuget package that does DI with Web API. See my answer. – garethb Jul 07 '15 at 06:25
24

You might want to take a look at the Unity.WebApi NuGet package which will sort all this out and also cater for IDisposable components.

see

http://nuget.org/packages/Unity.WebAPI

or

http://www.devtrends.co.uk/blog/introducing-the-unity.webapi-nuget-package

Paul Hiles
  • 9,558
  • 7
  • 51
  • 76
  • 2
    There's a good blog post here (http://netmvc.blogspot.com/2012/04/dependency-injection-in-aspnet-mvc-4.html) that has a walkthrough of using a combination of Unity.mvc3 (works with MVC4, too) and Unit.WebApi to implement DI pretty cleanly. – Phred Menyhert Aug 25 '12 at 20:52
  • 1
    The link mentioned in the comment reflects the updated API, for anybody who comes across this now: GlobalConfiguration.Configuration.ServiceResolver.SetResolver( new Unity.WebApi.UnityDependencyResolver(container)); is now GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container); – Robert Zahm May 13 '13 at 16:07
  • Take note, it also provides better debugging messages than the custom `UnityResolver`. – Ioannis Karadimas Sep 16 '15 at 07:01
11

Microsoft have created a package for this.

Run the following command from package manager console.

install-package Unity.AspNet.WebApi

If you already have Unity installed, it will ask if you want to overwrite App_Start\UnityConfig.cs. Answer no and continue.

No need to change any other code and DI (with unity) will work.

garethb
  • 3,951
  • 6
  • 32
  • 52
  • can you let me know why we answer 'no' when the nuget prompts to overwrite UnityConfig.cs? Question 2: is there some sample codes to use Unity.AspNet.WebApi? – Thomas.Benz Oct 10 '15 at 14:09
  • Sorry, I have changed to Autofac since there was some things I needed that Unity didn't do easily and Autofac did out of the box. But from memory you need to say no if you already have unity installed as you don't want to overwrite your already working unity config, yuo only want to add the unity webapi config. To use unity in webapi is exactly how you would use it normally. If its not injecting your dependencies as it does for your other classes, I'd suggest post a question. There is no different way of injecting into webapi than other classes (like controllers) – garethb Oct 12 '15 at 03:01
  • @garethb: I am using unity.webapi but have to add few lines of code in unit tests to resolve ControllerContext which I don't like. Suppose, if I use Unity.AspNet.WebApi, would that address resolving ControllerContext automatically from within UnitTests project? Please suggest – sam Apr 26 '16 at 22:08
  • I don't think so. Pretty sure both work in a similar way. I create a new mock context in my unit tests. – garethb Apr 26 '16 at 22:56
  • @garethb: Thank you for your quick response. mocking worked. I am unable to inject UrlHelper using either Unity.WebApi or Unity.Aspnet.webapi. Do you remember how you did it when you were using Unity? Thank you in advance – sam Apr 28 '16 at 17:27
  • @Nattrass This installs Microsofts implementation of Unity for Web API. I haven't looked through the actual code on how it works though I'm sure you can find it on github? – garethb Jan 19 '17 at 02:29
3

I had the same error and was searching the internet for solutions a couple of hours. Eventually it appeared that I had to register Unity BEFORE I was calling the WebApiConfig.Register. My global.asax now looks like

public class WebApiApplication : System.Web.HttpApplication
{
   protected void Application_Start()
   {
       UnityConfig.RegisterComponents();
       AreaRegistration.RegisterAllAreas();
       GlobalConfiguration.Configure(WebApiConfig.Register);
       FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
       RouteConfig.RegisterRoutes(RouteTable.Routes);
       BundleConfig.RegisterBundles(BundleTable.Bundles);
   }
}

For me this solved the issue that Unity could not resolve the dependencies in my controllers

Paritosh
  • 11,144
  • 5
  • 56
  • 74
ViKeR81
  • 121
  • 1
  • 3
2

In a recent RC, I find that there is no longer a SetResolver method. To enable both IoC for controller and webapi, I use Unity.WebApi (NuGet) and the following code:

public static class Bootstrapper
{
    public static void Initialise()
    {
        var container = BuildUnityContainer();

        GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);

        ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new ControllerActivator()));
    }

    private static IUnityContainer BuildUnityContainer()
    {
        var container = new UnityContainer();

        container.Configure(c => c.Scan(scan =>
        {
            scan.AssembliesInBaseDirectory();
            scan.With<UnityConfiguration.FirstInterfaceConvention>().IgnoreInterfacesOnBaseTypes();
        }));

        return container;
    }
}

public class ControllerActivator : IControllerActivator
{
    IController IControllerActivator.Create(RequestContext requestContext, Type controllerType)
    {
        return GlobalConfiguration.Configuration.DependencyResolver.GetService(controllerType) as IController;
    }
}

I also use UnityConfiguration (also from NuGet) for IoC magic. ;)

Noogen
  • 21
  • 1
  • Wouldn't it be better to use requestContext.Configuration.DependencyResolver.GetService()? – Alwyn Mar 27 '13 at 01:57
2

After reading the answers, I still had to do a lot of digging around to land at this gotcha so here it is for the benefit of peers: This is all you need to do in ASP.NET 4 Web API RC (as on 8th August '13):

  1. Add a reference to "Microsoft.Practices.Unity.dll" [I'm on version 3.0.0.0, added through NuGet]
  2. Add a reference to "Unity.WebApi.dll" [I'm on version 0.10.0.0, added through NuGet]
  3. Register your type mappings with the container - similar to the code in Bootstrapper.cs that gets added to your project by Unity.WebApi project.
  4. In the controllers that inherit from the ApiController class, create parameterized constructors that have their parameter types as the mapped types

Lo and behold, you get the dependencies injected into your constructor without another line of code!

NOTE: I got this information from one of the comments in THIS blog by it's author.

Sudhanshu Mishra
  • 6,523
  • 2
  • 59
  • 76
2

Short summary for ASP.NET Web API 2.

Install Unity from NuGet.

Create a new class called UnityResolver:

using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;

public class UnityResolver : IDependencyResolver
{
    protected IUnityContainer container;

    public UnityResolver(IUnityContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        container.Dispose();
    }
}

Create a new class called UnityConfig:

public static class UnityConfig
{
    public static void ConfigureUnity(HttpConfiguration config)
    {
        var container = new UnityContainer();
        container.RegisterType<ISomethingRepository, SomethingRepository>();
        config.DependencyResolver = new UnityResolver(container);
    }
}

Edit App_Start -> WebApiConfig.cs

public static void Register(HttpConfiguration config)
{
    UnityConfig.ConfigureUnity(config);
    ...

Now it will work.

Original source but a bit modified: https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection

Ogglas
  • 62,132
  • 37
  • 328
  • 418
1

This issue occurs due to registering Controllers with Unity. I solved this by using registration by convention as shown below. Please filter out any additional types as required.

    IUnityContainer container = new UnityContainer();

    // Do not register ApiControllers with Unity.
    List<Type> typesOtherThanApiControllers
        = AllClasses.FromLoadedAssemblies()
            .Where(type => (null != type.BaseType)
                           && (type.BaseType != typeof (ApiController))).ToList();

    container.RegisterTypes(
        typesOtherThanApiControllers,
        WithMappings.FromMatchingInterface,
        WithName.Default,
        WithLifetime.ContainerControlled);

Also the example above uses AllClasses.FromLoadedAssemblies(). If you are looking at loading assemblies from base path, it might not work as expected in a Web API project using Unity. Please take a look at my response to another question related to this. https://stackoverflow.com/a/26624602/1350747

Community
  • 1
  • 1
1

I had the same exception being thrown, and in my case I had a conflict between MVC3 and MVC4 binaries. This was preventing my controllers from being registered properly with my IOC container. Check your web.config and make sure it's pointed to the correct versions of MVC.

0

I had the same issue when using the Unity.WebAPI NuGet package. The problem was that the package never added a call to UnityConfig.RegisterComponents() in my Global.asax.

Global.asax.cs should look like this:

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        UnityConfig.RegisterComponents();
        ...
    }
}
Keith
  • 20,636
  • 11
  • 84
  • 125