Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Nininject MVC with class libraries

I'm quite new to IoC frameworks so please excuse the terminology.

So what I have is a MVC project with the Nininject MVC references. I have other class libarys in my project e.g. Domain layer, I would like to be able to use the Ninject framework in there but all of my bindings are in the NinjectWebCommon.cs under the App_Start folder in the MVC project:

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind<IHardwareService>().To<WindowsHardwareService>();
    kernel.Bind<IStatusApi>().To<StatusApiController>();
}

Currently in my class library I am using constructor injection but sometime I am having to hardcode the dependencies:

var service = new WindowsHardwareService();

When I would like to be able to do the following:

IKernel kernel = new StandardKernel(.....);
var context = kernel.Get<IHardwareService>();

I have not been doing the following because I do not have any modules? All of the documentation I have read is mainly aimed at the regular Ninject library and not the MVC version.

What do I need to do, and how can I use the regular Ninject library with the MVC version?

Update

This is what I have tried:

The aim of this is so that each project can load the module and get the current injected interface.

App_Start/NinjectWebCommon.cs (In MVC Project)

private static void RegisterServices(IKernel kernel)
{
    var modules = new IoCModules();
    var newKernal = modules.GetKernel();

    kernel = newKernal;
}

IoCModules.cs (In Project.Ioc project)

public class IoCModules
{
    public IKernel GetKernel()
    {
        var modules = new CoreModule();
        return modules.Kernel;
    }
}

CoreModule.cs (In Project.IoC.Modules project) <-- This is where all the references to all projects are, this get's around any circular dependency issues.

public class CoreModule : NinjectModule
{
    public override void Load()
    {
       Bind<IHardwareService>().To<WindowsHardwareService>();
       Bind<IStatusApi>().To<StatusApiController>();
    }
}

But I am currently getting the following:

Error activating IHardwareService

No matching bindings are available, and the type is not self-bindable. Activation path:

2) Injection of dependency IHardwareService into parameter service of constructor of type DashboardController

1) Request for DashboardController

Suggestions:

1) Ensure that you have defined a binding for IHardwareService.

2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.

3) Ensure you have not accidentally created more than one kernel.

4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.

5) If you are using automatic module loading, ensure the search path and filters are correct.

like image 967
Jamie Rees Avatar asked Nov 18 '15 09:11

Jamie Rees


1 Answers

It seems that you have a lot of questions what needs to be answered here, so I will try to do my best.

Based on your current question I will try to "draw up" a simplified architecture of your current implementation:

  • Domain layer: The core of your domain, place of your business entities, etc.
  • Infrastructure layer: This is where your services reside e.g.: WindowsHardwareService
    • IOC: I tend to call to this as DependencyResolution assembly.
  • UI: MVC application

Assuming this all above, we can state that your applications Composition Root or Entry point is the UI MVC project. One of the main concepts using a DI Container that is you initalize it in the Composition Root set up/do all your needed bindings and registrations here. The main intention to do it in the entry point is to avoid the Service Locator anti-pattern.

By using a DI Container you don't new() up your class implementations or get the kernel but rather ask for the registered dependency, following the rule of Inversion Of Control or also known as the Hollywood principle.

After the philosphy course, we can finally get to some actual implementation.

Creating an Ninject module: in your IOC assembly, lets call this file as ServiceModule.cs

using Ninject.Modules;
public class ServiceModule : NinjectModule
{
    public override void Load()
    {
        Bind<IHardwareService>().To<WindowsHardwareService>();
        Bind<IStatusApi>().To<StatusApiController>();
    }
}

This will be the Ninject module that you will register/load in the Composition Root.

Now about the Composition Root: in UI MVC projects NinjectWebCommon.cs You can have a method that is responsible loading your modules as below.

private static void RegisterServices(IKernel kernel)
{
    var modules = new List<INinjectModule>
        {
            new ServiceModule()
            //, new FooModule()
            //, new BarModule()
        };

    kernel.Load(modules);
}  

And finally your DashboardController in UI MVC:

public class DashboardController : Controller
{
    private readonly IHardwareService _hardwareService;

    public DashboardController(IHardwareService hardwareService)
    {
        _hardwareService = hardwareService;
    }
}

At this point, your ask for the registered implementation of IHardwareService in the controllers constructor. The DI Container will do the dirty job and pass you the instance that you can work with later in your controller.

A note about the interfaces: I tend to put these into an own assembly, where I just store the interfaces, e.g.: Project.Domain.Interfaces or Project.Infrastructure.Interfaces where each of these assemblies contain only domain or infrastructure interfaces.

References between assemblies:

To put all these together the UI only references the IOC assembly and the interfaces assembly that containts the interfaces you bound in your Ninject Module.

Summarizing all of the above:

Your classes and interfaces alone by theirselves are just pieces what are getting glued together by the DI container.

Hope I cleared it up a bit.

EDIT: as some good advice that @AndreySarafanov pointed out in comments, if you need different implementations of an interface you ask for in the constructor, you can use a Ninject Factory. For more information you can refer to this answer.

like image 142
kayess Avatar answered Oct 11 '22 15:10

kayess