Configuring Ninject with Asp.Net MVC & Web Api

50,725

Solution 1

After searching a lot, it turns out there we can't use Ninject with web api and regular mvc. I mean, we have to configure the Repositories separately.

I then found a nice article which explains how you can use Ninject with asp.net mvc & web api: http://www.codeproject.com/Articles/412383/Dependency-Injection-in-asp-net-mvc4-and-webapi-us

And now, I don't get the error and it's working :D

Update 1:

Also try Writing a simple implementation of dependency injection in MVC 4 Web API with .NET Framework 4.5

Solution 2

I have written some gists to help configure Ninject with MVC and Web Api. Simply include the file(s):

To add Bindings for concrete Types, Just put them in the Load() method of the MainModule. You can create as many modules as you like to keep bindings organized. but you'll also have to add them to the array that is returned in the Modules property.

Then Add to the Application_Start() method

  • NinjectContainer.RegisterModules(NinjectModules.Modules) (for MVC)
  • NinjectHttpContainer.RegisterModules(NinjectHttpModules.Modules) (for WebApi)

Note that you can use the same NinjectModules.Modules for both the MVC and WebApi registration. I just separated it for clearity

UPDATE: Remember to Remove NinjectWebCommon.cs from your project as it loads and bootstraps a new kernel at Runtime which unfortunately is only for MVC.

UPDATE: You can also use

  • NinjectContainer.RegisterAssembly() (for MVC)
  • NinjectHttpContainer.RegisterAssembly() (for WebApi)

This will scan your current assembly for all modules. This way you can put your modules anywhere in your project and it will be registered

Solution 3

With MVC 5 and Web API 2.2 I solved this problem by making sure I included the following NuGet packages:

  • Ninject.MVC5
  • Ninject.Web.WebApi.WebHost for Web API

This installed other Ninject dependencies and allowed me to RegisterServices through NinjectWebCommon.cs.

Solution 4

Here is the simple solution that works fine for me:

  1. In Visual studio, create new web application project named DemoApp and make sure you have selected Empty template with MVC and Web API references: enter image description here
  2. In Package manager console execute one by one:

    Install-Package Ninject

    Install-Package Ninject.MVC5

  3. Add NinjectDependencyResolver.cs to IoC folder :

    using Ninject;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Http.Dependencies;
    
    namespace DemoApp.IoC
    {
        public class NinjectDependencyResolver : IDependencyResolver, System.Web.Mvc.IDependencyResolver
        {
            private readonly IKernel kernel;
    
            public NinjectDependencyResolver(IKernel kernel)
            {
                this.kernel = kernel;
            }
    
            public IDependencyScope BeginScope()
            {
                return this;
            }
    
            public object GetService(Type serviceType)
            {
                return kernel.TryGet(serviceType);
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                return kernel.GetAll(serviceType);
            }
    
            public void Dispose() { } //it is not necessary to implement any dispose logic here
        }
    }
    
  4. Make the following changes in App_Start/NinjectWebCommon.cs :

    • Add these lines in CreateKernel method:

      NinjectDependencyResolver ninjectResolver = new NinjectDependencyResolver(kernel); DependencyResolver.SetResolver(ninjectResolver); //MVC GlobalConfiguration.Configuration.DependencyResolver = ninjectResolver; //Web API

    • Add your bindings in RegisterServices method like:

      kernel.Bind< IHelloService>().To< HelloService>();

Now NinjectWebCommon.cs should look like:

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(DemoApp.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(DemoApp.App_Start.NinjectWebCommon), "Stop")]

namespace DemoApp.App_Start
{
    using System;
    using System.Web;

    using Microsoft.Web.Infrastructure.DynamicModuleHelper;

    using Ninject;
    using Ninject.Web.Common;
    using DemoApp.IoC;
    using System.Web.Mvc;
    using System.Web.Http;
    using DemoApp.Config;

    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();
            try
            {
                kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
                kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

                RegisterServices(kernel);

                NinjectDependencyResolver ninjectResolver = new NinjectDependencyResolver(kernel);
                DependencyResolver.SetResolver(ninjectResolver); //MVC
                GlobalConfiguration.Configuration.DependencyResolver = ninjectResolver; //Web API

                return kernel;
            }
            catch
            {
                kernel.Dispose();
                throw;
            }
        }

        /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<IHelloService>().To<HelloService>();
        }        
    }
}
  1. Just for the completeness of the example, add some Mvc and Api controllers, and code for IHelloService, HelloService :

HomeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using DemoApp.Config;
namespace DemoApp.Controllers
{
    public class HomeController : Controller
    {
        private IHelloService helloService;

        public HomeController(IHelloService helloService)
        {
            this.helloService = helloService;
        }

        // GET: /Home/
        public string Index()
        {
            return "home/index: " + helloService.GetMessage();
        }
    }
}

UserController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using DemoApp.Config;
namespace DemoApp.Controllers
{
    public class UserController : ApiController
    {
        private IHelloService helloService;

        public UserController(IHelloService helloService)
        {
            this.helloService = helloService;
        }

        [HttpGet]
        public string Data()
        {
            return "api/user/data: " + helloService.GetMessage();
        }
    }
}

IHelloService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DemoApp.Config
{
    public interface IHelloService
    {
       string GetMessage();
    }
}

HelloService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DemoApp.Config
{
    public class HelloService : IHelloService
    {
        public string GetMessage()
        {
            return "Hi";
        }
    }
}
  1. The final structure should look like: enter image description here

    Now make some tests in browser. For me it was:

    http://localhost:51156/home/index

    http://localhost:51156/api/user/data

And that's it.

Share:
50,725
Idrees Khan
Author by

Idrees Khan

Well, in short. I love working with new technologies in Web, Mobile and Desktop. Techs such Angular, React, .Net, Xamarin, Ionic, Angular, Node are the one i am mostly targeting. I love building new things (of course not buildings) and i am very passionate by playing with many .js frameworks.

Updated on September 28, 2020

Comments

  • Idrees Khan
    Idrees Khan over 3 years

    i have setup my project with Ninject IoC.
    My project has regular Asp.Net MVC controllers and Web Api controllers. Now, Ninject works with Web Api but Ninject doesn't work with regular Asp.MVC controllers.
    My regular MVC controller implementation;

    public class GalleryController : BaseController
    {
        public GalleryController(IUow uow)
        {
            Uow = uow;
        }
        ........
    }
    

    Error when using with regular controller

    An error occurred when trying to create a controller of type 'Web.Controllers.HomeController'. Make sure that the controller has a parameterless public constructor.]
    

    However, when i try the same code with Web Api, it works

    public class GalleryController : BaseApiController
    {
        public GalleryController(IUow uow)
        {
            Uow = uow;
        }
        ......
    }
    

    my interface which holds difference repositories (the factory pattern)

    public interface IUow
    {
        // Save pending changes to the data store.
        void Commit();
    
        //Repositoryries
        IRepository<Gallery> Gallery { get; }
        IMenuRepository Menus { get; }
    }
    

    NinjectDependencyScope class;

    public class NinjectDependencyScope : IDependencyScope
    {
        private IResolutionRoot resolver;
    
        internal NinjectDependencyScope(IResolutionRoot resolver)
        {
            Contract.Assert(resolver != null);
    
            this.resolver = resolver;
        }
    
        public void Dispose()
        {
            var disposable = resolver as IDisposable;
            if (disposable != null)
                disposable.Dispose();
    
            resolver = null;
        }
    
        public object GetService(Type serviceType)
        {
            if (resolver == null)
                throw new ObjectDisposedException("this", "This scope has already been disposed");
    
            return resolver.TryGet(serviceType);
        }
    
        public IEnumerable<object> GetServices(Type serviceType)
        {
            if (resolver == null)
                throw new ObjectDisposedException("this", "This scope has already been disposed");
    
            return resolver.GetAll(serviceType);
        }
    }
    

    NinjectDependencyResolver class;

    public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
    {
        private IKernel kernel;
    
        public NinjectDependencyResolver(IKernel kernel)
            : base(kernel)
        {
            this.kernel = kernel;
        }
    
        public IDependencyScope BeginScope()
        {
            return new NinjectDependencyScope(kernel.BeginBlock());
        }
    }
    

    Ninject configuration for Global.asax;

    public class IocConfig
    {
        public static void RegisterIoc(HttpConfiguration config)
        {
            var kernel = new StandardKernel(); // Ninject IoC
            //kernel.Load(Assembly.GetExecutingAssembly()); //only required for asp.net mvc (not for webapi)
            // These registrations are "per instance request".
            // See http://blog.bobcravens.com/2010/03/ninject-life-cycle-management-or-scoping/
    
            kernel.Bind<RepositoryFactories>().To<RepositoryFactories>()
                .InSingletonScope();
    
            kernel.Bind<IRepositoryProvider>().To<RepositoryProvider>();
            kernel.Bind<IUow>().To<Uow>();
    
            // Tell WebApi how to use our Ninject IoC
            config.DependencyResolver = new NinjectDependencyResolver(kernel);
        }
    }
    

    Global.asax

    protected void Application_Start()
    {
    
        // Tell WebApi to use our custom Ioc (Ninject)
        IocConfig.RegisterIoc(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    
        GlobalConfig.CustomizeConfig(GlobalConfiguration.Configuration);
        AreaRegistration.RegisterAllAreas();
    }
    
  • Idrees Khan
    Idrees Khan about 11 years
    then why it's working with web api?. i just think that there is some settings which i am missing in my classes that only works with asp.net mvc regular controllers
  • epalm
    epalm over 10 years
    I've followed your instructions to support both MVC and WebApi. Confusion #1: Your gists say to include Ninject.Web.Common, which drops NinjectWebCommon.cs in App_Start, which by default creates a new StandardKernel. Both NinjectResolver and NinjectHttpResolver create their own StandardKernel object too. So I have 3 kernels running around?
  • epalm
    epalm over 10 years
    Confusion #2: After defining an appropriate constructor parameter in an MVC controller, I'm getting No parameterless constructor defined for this object when running the app. Not sure where to go from here.
  • Ody
    Ody over 10 years
    Ok, you should get rid of the Ninject.Web.Common.cs its trying to automatically generate a kernel and wire bindings. Unfortunately these bindings and Kernel are only for MVC. As for the duplicate Kernels, I wrote it that way because you might only need to wire Ninject for either MVC or WebAPI. In your case, you can replace the creation of the kernel with a call to a GetKernel() on a central static class
  • TimS
    TimS over 10 years
    @Ody Do you have an example of how to do this as you're describing?
  • Matt Baker
    Matt Baker over 9 years
    @Ody when using your implementation for MVC5 and Web API 2 MVC works fine (after unbinding ModelValidatorProvider), but for Web API I am getting: "Error activating IFilterProvider using binding from IFilterProvider to DefaultFilterProvider A cyclical dependency was detected between the constructors of two services." Any ideas?
  • Matt Baker
    Matt Baker over 9 years
    @Ody apparently you should never reference Ninject.Web.WebApi, just Ninject.Web.Mvc
  • Ody
    Ody over 9 years
    Interesting... As for your earlier question, I usually debug such issues using a "Unit Test" which just loads the modules (shown above) and then trys to get a single service NinjectContainer.Resolve<IService>(). The Ninject folks did a great job in showing the activation path in the exception that is thrown. That way, I can tell what's causing the cyclic dependency
  • Robert Kaucher
    Robert Kaucher over 9 years
    Could you add some rough details about what you did in case that blog post goes away?
  • Vladislav Kostenko
    Vladislav Kostenko almost 9 years
    Does your solution respect OnPerRequest bindings?
  • Ody
    Ody over 8 years
    Nope it doesnt. Eventually, Ninject disposes of the objects whenever IIS disposes your HttpContext. But if you want more deterministic disposal, you can register the OnePerRequest module in your web.config. You can look at this question for more information Do I need to register Ninject.OnePerRequestModule with Ninject.Web.MVC 3.0?
  • Rob Prouse
    Rob Prouse about 8 years
    Thanks. A much cleaner solution than the earlier ones. This should probably be marked as the current correct answer.
  • Give IT
    Give IT almost 8 years
    man please, tell us how you did it.. I have exactly the same problem!
  • Mr Posia
    Mr Posia over 7 years
    Great examples! Great answer! Thanks!
  • Evan
    Evan almost 7 years
    Note: if you get an error about multiple bindings for HttpConfiguration, double check the version of one of the dependent nuget assemblies -Ninject.Web.WebApi. Update that one to the latest version. See this answer
  • Jean-Paul
    Jean-Paul over 5 years
    Great example! I have one question, shouldnt UserController also have a IUserController?
  • r23
    r23 about 5 years
    @Jean-Paul, in this simple example no, because usually controller is starting point where we inject other objects, but if you need to use something like IUserController either for some kind of injection or you would like this interface to be shared among other controllers then you can try.
  • Marco Thomazini
    Marco Thomazini over 3 years
    The link on "Update 1" is broken, but I found this copy on Web Archive - web.archive.org/web/20160124235319/http://blogs.msdn.com/b/…