ASP.NET Web API binding with ninject
Asked Answered
L

11

37

I have just installed the mvc4 rc update and I am trying to build an api application with little luck.

I am using ninject but cant get my controllers to load. I keep getting an error

Type 'Api.Controllers.ConsumerController' does not have a default constructor

I am very new to mvc and using injection so please bear with me.

I havent done anything special to the default binding that is created via nuget

 public static class NinjectWebCommon 
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start() 
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        bootstrapper.Initialize(CreateKernel);
    }

    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);
        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IConsumerRepository>().To<ConsumerRepository>();
    }        
}

My controller looks like

   private readonly IConsumerRepository _repository;

    public ConsumerController(IConsumerRepository repository)
    {
        _repository = repository;
    }

    [HttpGet]
    public IQueryable<Consumer> Get(Guid id)
    {
        return _repository.Get(id).AsQueryable();
    }

What do I need to do to get the api controllers to work with ninject?

Sorry if this is simple stuff

I tried your suggestion Michael however after changing the the webcommon.cs to this

  private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);
        GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IConsumerRepository>().To<ConsumerRepository>();
    }

I get an error when

var kernel = new StandardKernel();

is called

Method 'GetFilters' in type 'Ninject.Web.WebApi.Filter.DefaultFilterProvider' from assembly 'Ninject.Web.WebApi, Version=3.0.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7' does not have an implementation.

What am I missing?

Langan answered 1/6, 2012 at 11:8 Comment(0)
T
60

I asked Brad Wilson about this and it has changed in MVC4 RC.

GlobalConfiguration.Configuration.ServiceResolver has been moved to GlobalConfiguration.Configuration.DependencyResolver

Use this implementation to create a Ninject DependencyResolver for your Web Api: https://gist.github.com/2417226

In NinjectWebCommon.cs:

// Register Dependencies
RegisterServices(kernel);

// Set Web API Resolver
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
Trucker answered 1/6, 2012 at 17:50 Comment(7)
Hi Michael when I use your suggestion I get an error Method 'GetFilters' in type 'Ninject.Web.WebApi.Filter.DefaultFilterProvider' from assembly 'Ninject.Web.WebApi, Version=3.0.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7' does not have an implementation.Langan
I removed the reference to webapi and it looks to have resolved it.Langan
I think you're using a Ninject plugin that is meant for an older version of WebApi. Remove that reference and only use Nuget packages Ninject, Ninject.Web.Common and Ninject.MVC3 (Still works for MVC4)Trucker
I was experiencing the same problem and removed the ninject's web.api reference just like you said. Now it works!Surety
I tried this but I'm still getting the "No parameterless constructor defined for this object." error. I also just upgraded from MVC 3 so maybe I missed something else...Suggestion
I had to follow the steps in strathweb.com/2012/05/…, which includes the step in this answer.Cleavable
I have been searching for this answer for so long, great work! I am surprised they don't update the nuget package to include this....J
M
6

This generic error message

Type 'Api.Controllers.ConsumerController' does not have a default constructor

can also occur if you do not make your constructor public, or the dependency cannot be resolved by the IoC container maybe because of a missing argument.

The error message is misleading to say the least.

Marisolmarissa answered 15/8, 2012 at 10:7 Comment(2)
So simple but the private constructor was my problem and I was checking everything but I couldn't see it, your hint saved me another day.Distribution
I had a constructor which requested an interface, which was bound to a class that requested another interface - that interface didn't have a binding and instead of letting me know that - it just said that the ApiController didn't have a default constructor - double check that your dependencies are all registered if you get this.Ganger
B
6

You can install the NuGet package WebApiContrib.IoC.Ninject and add the following line of code to NinjectWebCommon.cs

GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
Borisborja answered 2/10, 2013 at 22:27 Comment(1)
This worked for me! Thanks... Also make sure to set your bindings in your NinjectWebCommon.cs file :)Steed
B
1

For someone landing here while searching '...does not have a default constructor' looking for an easy way to debug silently failing configurations:

  1. Remove constructor-injected objects until the constructor can be invoked
  2. For each of the previously injected, now uninitialized objects, invoke using:

ServiceLocator.Current.GetInstance<[OBJ-TYPE]>()

The offending object will cause an ActivationException with a descriptive Message. You'll have something to go for.

Remember to remove call to ServiceLocator post-fix. Ie. this is not a recommendation to use the service locator anti pattern outside debugging.

Boltzmann answered 24/2, 2016 at 14:16 Comment(0)
G
0

have you registered the container with the frawework? I prefer using autofac, here is an example of how to use autofac with API. http://alexmg.com/post/2012/03/08/Autofac-ASPNET-Web-API-%28Beta%29-Integration.aspx

Also, Mark Seeman has a good post on DI in general with WebAPI

http://blog.ploeh.dk/2012/03/20/RobustDIWithTheASPNETWebAPI.aspx

From Ploeh:

GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
    t => this.container.Kernel.HasComponent(t) ?
        this.container.Resolve(t) :
        null,
    t => this.container.ResolveAll(t).Cast<object>());

The above has to be performed in the global.asax

Gusgusba answered 1/6, 2012 at 13:34 Comment(0)
H
0

Hopefully this helps someone else...

I was having the same issue and it was related to me moving class responsible for registering assembly in charge of initializing controllers. Moved out of web into framework project.

Using Autofac but same would apply for other containers.

Was calling:

builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

Which works fine when it's within web application, but threw above exception when moved to framework project as the executing assembly no longer contains the controllers.

Instead had to update to:

builder.RegisterApiControllers(Assembly.GetCallingAssembly());
Hotheaded answered 20/8, 2012 at 2:13 Comment(0)
A
0

It seems Ninject didn't throw an exception as it generally does when your IOC dependencies aren't quite set up right. Instead it made it look like I hadn't registered the WebAPI dependency resolver which I certainly did. Here was my solution to this problem but from what I've found it could be MANY DIFFERENT types of setup issues. Just re-check everything in the dependency chain. Hopefully it helps someone!

The controller:

public class ContestsController : ApiController
{
    //Ninject wouldn't inject this CTOR argument resulting in the error
    public ContestsController(IContestEntryService contestEntryService)
    {

The dependency:

public class ContestEntryService : IContestEntryService
{

    public ContestEntryService(IContestsContext contestsContext)
    {

The incorrect configuration:

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IContestsContext>()
              .To<ContestsContext>()
              .InRequestScope();

        kernel.Bind(x =>
            x.FromAssembliesMatching("MyNameSpace.*")
             .SelectAllClasses()
             .BindAllInterfaces()
         );

The correct configuration:

    private static void RegisterServices(IKernel kernel)
    {
         kernel.Bind(x =>
            x.FromAssembliesMatching("MyNameSpace.*")
             .SelectAllClasses()
             .BindAllInterfaces()
         );

         kernel.ReBind<IContestsContext>()
              .To<ContestsContext>()
              .InRequestScope();

Generally Ninject is pretty good about reporting these sorts of errors so I really got thrown for a loop on this one!

Admass answered 6/9, 2013 at 18:17 Comment(0)
S
0

if any anyone is still having problems, please listen for some reason ninject is not working how we would expect with mvc 4. In your web api, you need to write this code

public DefaultController() : base() { }  

This removes the error saying about no default constructor, then when you need to get your data from the get method write this code:

public IEnumerable<YourModelGoesHere> Get() 
{
    return context.YourData;  
} 

Keep in mind, you will have to access your db class here as well, for instance:

DefaultConnection context = new DefaultConnection(); 
Successful answered 13/2, 2014 at 0:53 Comment(0)
S
0

just install Ninject.MvcXXX package, where XXX - version of MVC...

Saxen answered 14/1, 2015 at 14:8 Comment(0)
B
0

I had this error message, too, but it just turned out that one of my interfaces weren't actually implemented by any classes (I forgot to add it to the class declaration).

Beal answered 24/2, 2015 at 18:11 Comment(0)
P
0

It worked after uninstalling "Microsoft.asp.net mvc4 runtime".

Pennsylvanian answered 21/6 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.