Unity IoC does not inject dependency into Web API Controller
Asked Answered
F

4

21

I'm very new to using Unity, but my problem is that whenever I call my web service, I get an exception stating that

"Make sure that the controller has a parameterless public constructor"

I've followed multiple tutorials and I still get the same issue.

In the Register function of my WebApiConfig class, I have

var container = new UnityContainer();
container.RegisterType<IValidator, Validator>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);

Here is my UnityResolver class

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()
    {
        container.Dispose();
    }
}

I have not registered any controllers, as every tutorial claims that I don't need to do this. Here is my actual controller

public class Controller: ApiController
{
    private IValidator _validator;

    public Controller(IValidator validator)
    {
        this._validator = validator;
    }

    [HttpPost]
    public void ReceiveIPN()
    {

    }
}

Does anyone have any ideas as to what I can be doing wrong? Thanks!

EDIT 1: Here is the "implementation" of the Validator class. It's pretty empty, because I didn't want to introduce a bug here until I resolved the Unity issue.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;


public class Validator: IValidator
{
    public bool ValidateIPN(string body)
    {
        throw new NotImplementedException();
    }
}

EDIT 2: Here is the entire error response I get when I attempt to call the web api route using Fiddler

{"message":"An error has occurred.","exceptionMessage":"An error occurred when trying to create a controller of type 'Controller'. Make sure that the controller has a parameterless public constructor.","exceptionType":"System.InvalidOperationException","stackTrace":" at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request) at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()","innerException":{"message":"An error has occurred.","exceptionMessage":"Type 'Project.Controller' does not have a default constructor","exceptionType":"System.ArgumentException","stackTrace":" at System.Linq.Expressions.Expression.New(Type type) at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"}}

Faris answered 13/4, 2015 at 17:8 Comment(26)
You have to register your controller with Unity which I don't see you doing. If you're doing that, or if that still doesn't work, total guess, but try naming your controller something other than controller. Sometimes reflection on a class name that shares the same name as something in the framework has weird side effects.Undesigning
@moarboilerplate: controllers don't have to be registered. \@Kith: can you show how your Validator looks like? Does it also take in a dependency?Rapier
I'm not registering the controller, and the controller isn't actually named Controller. I changed the name before posting here. @JeroenVannevel I can post the Validator, but it's actually empty lol. I didn't define a constructor explicitly on it, and it only has one method that throws a not implemented exception. I pretty much stopped at this pointFaris
Does it work when you remove the dependency in your controller's constructor?Rapier
@JeroenVannevel Yes it doesFaris
Then the problem must be located in the validator. Can you post the implementation of Validator?Rapier
@JeroenVannevel Ah right.Undesigning
@JeroenVannevel I've added the validator "implementation". It's pretty empty though. Thanks for taking the timeFaris
I've looked at most things I can think of that typically cause this. Can you share the exact stacktrace? Maybe that contains any clues.Rapier
@JeroenVannevel I've updated the question with the trace.Faris
The error seems to be clear. Make sure your controller class has a default (parameterless) constructor. Try that and see what happens.Masqat
@Masqat Although that does work, it doesn't solve my problem. If I add a parameterless constructor, that's the constructor that ends up being used, and my Validator dependency isn't injected into my Controller. The whole point of my using Unity is to be able to inject my dependencies in, for unit testing purposes.Faris
@Kith: according to this answer you can get a more detailed exception message if you explicitly register your controllers. If that doesn't yield anything though, throw your solution in a zip file and upload it somewhere so I can take a look around locally (or put it on github).Rapier
Sorry I completely misread your question and you're completely right. My bad. Based on this sample codeproject.com/Articles/797132/… you may need to do this in a custom DefaultControllerFactory but I can't say for sure. Again, my apologies for not understanding the question properly.Masqat
Did you make sure your WebApiConfig.Register method is invoked?Ergosterol
@JeroenVannevel It doesn't look like explicitly registering my controller changed the exception message. Also, I really can't share the entire project for legal reasons, as much as I'd love to (to get this figured out lol). Sorry about that, and I appreciate all the help you've given me so far.Faris
@Masqat No worries :-). I looked into the DefaultControllerFactory, but it looks like that's not the ideal approach (at least, any more). If I can't figure out the DependencyResolver approach, I'll definitely look into that.Faris
@Ergosterol Yes I did. I put a break-point there, and it's always reached when i first start up the project and call the web service.Faris
Perhaps a shot in the dark, but try to change your controller name from Controller to something like MyController.Ergosterol
@Kith: I suggest you create a minimal example that reproduces the problem. If you can do that then you can share it with us -- if you can't do that then you can gradually build up your project and you'll notice eventually where things break down.Rapier
@Ergosterol I've changed the name of that controller many times and it doesn't seem to be the issue. Thanks though :-)Faris
@JeroenVannevel Will do. I'll post back as soon as I have any other clues. Thanks again.Faris
@Kith, Could it be that RegisterType<IValidator, Validator> is actually referring to System.Web.WebPages.IValidator? Or Validator is actually referring to System.Web.WebPages.Validator? Or both?Ergosterol
@Tom, that's not passing it to the ctor, it merely sets the lifetime.Ergosterol
Dumb question, but are you making sure you're calling your WebApiConfig.Register method from Global.asax?Undesigning
@JeroenVannevel I managed to figure out what was going on and posted the solution as an answer. Thanks for the helpFaris
F
26

So, after hours of banging my head against a wall, I've found that this wasn't working because I had an OWIN/Katana Startup.cs class in my project. Now I don't know exactly what's going on here, so any more information would be great.

Basically, since I was using OWIN/Katana, I had a Startup.cs file that created a new HttpConfiguration object and configured it, similar to how it's done in the WebApiConfig.cs class.

private void ConfigureWebApi(HttpConfiguration config)
{
    config.MapHttpAttributeRoutes();
    var container = new UnityContainer();
    container.RegisterType<IValidator, Validator>();
    config.DependencyResolver = new UnityDependencyResolver(container);

    var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter().First();
    jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}

It looks like the code first runs through the WebApiConfig's Register function, and THEN overrides that HttpConfiguration object with the one generated in the Startup.cs file. I had to move my container setup stuff here in order for it to work.

Sorry that I didn't bring up the OWIN stuff before. This is fairly new to me and I didn't realize it was relevant. Hopefully this saves someone else the pain that I've just been through.

Faris answered 13/4, 2015 at 20:15 Comment(4)
Important detail indeed ;) Glad you were able to solve it though. When using Owin the order of operations is pretty important for some aspects (things like WebApiConfig.Register() and app.UseWebApi() have to be in that order and at the end of the StartUp class).Rapier
Hi, I ran into the same problem, solution was to simply pass the config object to the Unity registration function and use the DependencyResolver from that instance: public static void Register(HttpConfiguration config) { var container = new UnityContainer(); // replace... /* GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container); */ // with this: config.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container); }Better
After wasting a lot of time on this, i found answer here. forums.asp.net/t/1982840.aspx?Web+API+2+1+Dependency+InjectionBlinny
if your project is WebApi + Standard views, then just install packages "unity.mvc5" and "unity.webapi". Then in UnityConfig.RegisterComponents type this: System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container); System.Web.Mvc.DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));Lucier
U
1

Your Unity container is probably getting disposed because it's only defined in the scope of your WebApiConfig.Register() method. If you define your container as a member of Global, which will keep your container around for the lifetime of the app, it should work.

Edit: also don't forget to dispose the container on Application_End.

Undesigning answered 13/4, 2015 at 18:29 Comment(1)
I'm certain this issue is due to scoping, but I'm not sure if the issue is due to the container falling out of scope due to the method scope, or if it's because the container isn't a static property accessible by all threads.Undesigning
H
1
  • Install Unity.mvc5 and Unity.WebApi in a project through nuget package
  • Specify fullnamespace as per below line

Example:

public static class UnityConfig
{
    public static void RegisterComponents()
    {
        var container = new UnityContainer();

        // register all your components with the container here
        // it is NOT necessary to register your controllers

        // e.g. container.RegisterType<ITestService, TestService>();

        DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));

        GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
    }
}
Homoeo answered 10/12, 2018 at 14:35 Comment(0)
B
-1

I know this is an old post but since I just ran into the same problem, the solution was to simply pass the config object to the Unity registration function and use the DependencyResolver from that instance:

EDIT: Just noted that the accepted answer does exacly this... Sorry for the spam ;-)

public static class UnityConfig
{
    public static void Register(HttpConfiguration config)
    {
        var container = new UnityContainer();

        // register your services here...

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

        // with this:
        config.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
    }
}
Better answered 17/11, 2016 at 21:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.