Using Nininject MVC with class libraries
Asked Answered
A

1

17

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.

Antebi answered 18/11, 2015 at 9:57 Comment(10)
Please see the following answer. I think it hits on similar points and will help you :).Simas
@Coulton Thanks for the suggestion but it doesn't really answer, I don't want to use Property Injection, I was to be able to use Ninject MVC with other class library's, That may include moving my Bindings to somewhere more 'common'.Antebi
Ok no problem. What kind of injection do you want to do if it's not property or constructor injection?Simas
Is the question is about how to create ninject modules for your own registrations and where to put and reference them from?Hippocras
@Hippocras Yes, I don't want my UI project referencing everything because I need to add the bindings in there, I need a 'common' project that has references to Ninject to be able to use it everywhere in the solution, while keeping the Ninject MVC part working.Antebi
Look at Ninject modules. You need a NinjectModule that you can call for each of your separate projects that sets up the bindings that are specific to each of your projects.Simas
Not sure that it's what you need, but still. In our project we create an MvcApplication : NinjectHttpApplication in Global.asax, create the Ninject Kernel in overridden CreateKernel method. If we want to resolve a class in a conroller not via constructor injection (which we never do though), we can do it via DependencyResolver.Current.GetService method. Plus in case you have to create and resolve objects in runtime, think about using Ninject.Extensions.Factory.Apparently
Oh, I didn't understand the question correctly at all(Apparently
@Apparently I updated to include what I have tried. It might be easier to see then.Antebi
If you follow the example the GetKernel method should look like this: return new StandardKernel(new CoreModule());. Not sure if it'll help but worth a tryApparently
H
18

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.

Hippocras answered 18/11, 2015 at 10:54 Comment(4)
Yes that makes a lot of sense, but I have the problem that for example I do not want the UI to reference the Infrastructure layer for SOC. I was attempting to create the IOC project to have all the references there and the UI will just reference the IOC project.Antebi
Excellent, but how can I access the Kernel in other projects? to do Kernel.Get<T> ?. Thinking about it, if everything is abstracted behind an interface and constructor injection is used, I should never need to kernel.Get<T> right?Antebi
Yes, exactly! You try and never do kernel.Get<>, you compose your object graph in the Compostion Root!Hippocras
I'll just add that in cases when you need to create objects in runtime, it's better to add factories as dependencies, so that you won't need Kernel.Get. In simple cases those factories can be autoimplemented (e.g. via Ninject.Extensions.Factory).Apparently

© 2022 - 2024 — McMap. All rights reserved.