Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ninject - What is the correct way to set up bindings in the domain model layer of an N-Tier MVC3 application?

I've started using Ninject in an ASP.NET MVC 3 project, using the standard boot strapper that comes with the nuget package, as below.

/// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
           // Documentation for setting up Ninject in ASP.NET MVC3 app: 
           // https://github.com/ninject/ninject.web.mvc/wiki/Setting-up-an-MVC3-application

           // Add bindings here. No modules required unless we need to add in further
           // separation of concerns.

           kernel.Bind<ISessionManager>().To<SessionManager>();
           kernel.Bind<ICookieManager>().To<CookieManager>();
           kernel.Bind<ILogManager>().To<LogManager>();
           kernel.Bind<IStringOutput>().To<StringOutput>();
    } 

Within my Domain Model layer, there is a class called CookieManager. Within my CookieManager class, I'm using a class called SecureObjectSerializer.

I want to use dependency injection so that the CookieManager is not tighly bound to the SecureObjectSerializer.

I don't want the MVC project to have to know about the SecureObjectSerializer, setting up the bindings etc. That seems to be taking DI too far to me.

I do however, think that within the Domain Model layer, the CookieManager should have SecureObjectSerializer passed in in a DI manner.

My questions:

  1. To have the binding registered in the Domain Model layer, should I create a Ninject bootstrapper within the Domain Model layer? If so, how should I have this triggered. Would I supply a hook and call something like DomainModel.BindModelDependencies(kernel); in the MVC project.

  2. When it's time to resolve a new instance of an object, what would this code look like? It's kind of hidden when using the MVC bootstrapper?

like image 211
gb2d Avatar asked Mar 20 '12 11:03

gb2d


2 Answers

You should put a NinjectModule inside of the domain assembly (not the web assembly) and you can tell ninject to scan your domain assembly and look for the module. module is just a class that inherits from NinjectModule base class and implements Load(). This way the web project only needs a reference to the domain project. the Scanning should look something like:

either of these two: (actually there are a few more options but these are the main ones i use)

var assemblysToScan = "Assembly.*";
var assemblysToScan = new[] { "Assembly.Domain", "Assembly.Web" };

and then just

kernel.Scan(x =>
                {
                    x.From(assemblysToScan)
                    x.AutoLoadModules();
                });

When you want to resolve an object, just put it as a ctor argument, and if ninject is managing you objects, it will inject the type (based on the bindings you have configured) automatically.

EDIT:

add references to ninjectcommonservicelocator (cant remember the exact name) & microsoft.practices.servicelocation

in your bootstrapper add these:

using Microsoft.Practices.ServiceLocation;
using CommonServiceLocator.NinjectAdapter;

var locator = new NinjectServiceLocator(kernel);
ServiceLocator.SetLocatorProvider(() => locator);

then in your class:

using Microsoft.Practices.ServiceLocation;

class CookieManager : ICookieManager
{
  SecureObjectSerialiser secureObjectSerialiser;

  CookieManager()
  {
    this.secureObjectSerialiser = ServiceLocator.Current.GetInstance<SecureObjectSerialiser>();
  }
}

the reason for the microsoft.practices locator is your code should not know that ninject is working under the covers. instead, you use a generic servicelocator that can be reused if you change containers.

personally i would avoid this and just inject everything. but if you cant, you cant.

like image 129
AaronHS Avatar answered Sep 24 '22 12:09

AaronHS


The accepted answer refers to the Scan() method from Ninject.Extensions.Conventions. Ninject 3.0 replaced this with a more powerful fluent interface for doing dynamic assembly binding.

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

The adventure continues at the extension's Wiki.

like image 41
kenchilada Avatar answered Sep 24 '22 12:09

kenchilada