Configuring Ninject with Asp.Net MVC & Web Api
Asked Answered
R

6

37

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();
}
Rosecan answered 5/5, 2013 at 9:3 Comment(0)
R
12

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

Rosecan answered 6/5, 2013 at 14:6 Comment(3)
Could you add some rough details about what you did in case that blog post goes away?Aerogram
man please, tell us how you did it.. I have exactly the same problem!Lavonia
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/…Suppress
M
33

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

Meeks answered 23/6, 2013 at 2:52 Comment(9)
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?Histology
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.Histology
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 classMeeks
@Meeks Do you have an example of how to do this as you're describing?Burke
@Meeks 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?Lupita
@Meeks apparently you should never reference Ninject.Web.WebApi, just Ninject.Web.MvcLupita
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 dependencyMeeks
Does your solution respect OnPerRequest bindings?Impracticable
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?Meeks
H
16

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.

Herpetology answered 29/5, 2015 at 17:15 Comment(2)
Thanks. A much cleaner solution than the earlier ones. This should probably be marked as the current correct answer.Incumbency
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 answerMitchel
R
12

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

Rosecan answered 6/5, 2013 at 14:6 Comment(3)
Could you add some rough details about what you did in case that blog post goes away?Aerogram
man please, tell us how you did it.. I have exactly the same problem!Lavonia
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/…Suppress
S
6

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.

Seemaseeming answered 27/12, 2016 at 3:4 Comment(3)
Great examples! Great answer! Thanks!Sopping
Great example! I have one question, shouldnt UserController also have a IUserController?Euphemia
@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.Seemaseeming
G
0

I think the issue is that you are not registering a ControllerFactory that uses Ninject to build the controllers (and resolve their dependencies), have you tried implementing your own ControllerFactory yet? See also here http://bubblogging.wordpress.com/2012/06/04/mvc-controller-factory-ninject/.

Gestapo answered 5/5, 2013 at 9:25 Comment(1)
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 controllersRosecan
C
0

There is a more elegant solution for this by Nenad - it took me 3 extra hours because I first tried to implement the solutions here conflicting with existing infrastructure I had. It is in reply to another stack overflow question. I am duplicating that answer here just in case it helps others to save the time I lost.


There is a way to share same container between MVC and ASP.NET Web API. You just need to implement both interfaces.

public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver, System.Web.Mvc.IDependencyResolver
   {
       private readonly IKernel kernel;

       public NinjectDependencyResolver(IKernel kernel)
           : base(kernel)
       {
           this.kernel = kernel;
       }

       public IDependencyScope BeginScope()
       {
           return new NinjectDependencyScope(this.kernel.BeginBlock());
       }
   }

Check this article for solution: Simple Way to share Dependency Resolvers between MVC and Web API

Crescen answered 21/10, 2016 at 11:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.