A short summary about DI and using Ninject not as a Service Locator

Note: This post was edited on 21st December 2011, after the first comment was left by Remo Gloor .


Using Ninject in a classic DI scenario, means that only the entry point of you code should have knowledge about Ninject. This is different than using it as a Service Locator, where either
  • all classes know about the IoC Container (Ninject, in this case) and access it, meaning that every class is implicitly coupled with it.
  • or, you have to pass the Service Locator as an argument to every class's constructor, which means adding a lot of code and having classes that don't use the SL directly know about it, since they must pass it along to classes they use.
So your code-entry point configures the Kernel to be used, according to its scenario. Then what?
Well, then it uses the IoC Container to instantiate a class, or several classes, and that's it. Behind the scenes, every class called by those classes, doesn't know about the IoC Container, but declares its dependencies in its constructor. When the class is instantiated, the IoC Container will recognize the dependencies and attempt to resolve them, i.e. instantiate the classes which this class needs.

Note these following cases:
  • Class A depends on class B, but only uses it in specific situations.
  • Class A needs to return a type of class B.
  • Class A depends on Class B, but the configuration of Class B will only be known at run-time.
  • Class A will need several instances of class B, but the number of instances will only be known at run-time. 
In these cases, you might want to pass a factory method to class A's constructor. This method can be used by class A to instantiate class B. So how do you do that?

Let's use a real-life example. Class B, in this example, is AuditManager, and it implements an interface called IAuditManager.
First, we declare the factory method in the constructor of the dependent class (called "Class A", above):
...
private readonly Func <IAuditManager> _auditManagerFactory;

public SomeConstructor(Func<IAuditManager> auditManagerFactory) 
{
     _auditManagerFactory = auditManagerFactory;
}
...


As you can see, we're using Func<> to declare the delegate for the factory method.
To use the method, we simply invoke it:

   var auditManager= _auditManagerFactory();     
   auditManager.AuditSomething();


And this is how to bind the method, in the NinjectModule:

Bind<Func<IClassB>>()
.ToMethod(ctx => () => ctx.Kernel.Get<ClassB>());