SoC, DI, IoC, WTF? A basic introduction to Dependency Injection with Ninject

3 Apr
2010

The latest TLA (Three Letter Acronyms) du jour seem to be SoC, DI, and IoC.  What do these stand for and why should you care about them?

Consider the following (very) simple console application:

Code Snippet
  1. using System;
  2.  
  3. namespace TightlyBoundTestApp
  4. {
  5.     public class Program
  6.     {
  7.         static void Main(string[] args)
  8.         {
  9.             // Load up a customer
  10.             Customer c = new Customer();
  11.  
  12.             // Logging should have written a message
  13.             c.Log();
  14.  
  15.             Console.ReadKey();
  16.         }
  17.     }
  18.  
  19.     public class Customer
  20.     {
  21.         private Logger logger;
  22.  
  23.         public Customer()
  24.         {
  25.             logger = new Logger();
  26.         }
  27.  
  28.         public void Log()
  29.         {
  30.             logger.LogMessage("This should be logged");
  31.         }
  32.     }
  33.  
  34.     public class Logger
  35.     {
  36.         public void LogMessage(string message)
  37.         {
  38.             Console.WriteLine("Log: " + message);
  39.         }
  40.     }
  41. }

In this sample application, we have a Customer class which (for example purposes) only has a single method: Log, which allows it to log a message. We might use this to keep track of the transactions done by a customer so we have an audit trail for example. The logging happens by calling the LogMessage method on an instance of the Logger class which the Customer class created in its constructor.

In our entry point to the program, we create a new instance of our Customer class, and tell it to log a message.  If we run this, a log message will be printed to the console, then the program will exit once we press a key.

So, it all works okay, but there a few problems which may cause issues for us down the track.  We have made our Customer class depend on our Logger class.  What happens when we want to create unit tests for our Customer class?  We would also have to consider our Logger class as they are tightly coupled.

This violates the first of our TLAs: Separation of Concerns (SoC). "Separation of Concerns is the process of separating a computer program into distinct features that overlap in functionality as little as possible. A concern is any piece of interest or focus in a program.” – Wikipedia

How do we go about achieving this separation then? Instead of having our Customer class depend on a concrete implementation of a Logger class, we could change it to interact with an interface instead. The principle behind this is known as Dependency Inversion – "High-level modules should not depend on low-level modules. Both should depend on abstractions." – Wikipedia.

By changing the Customer class to interact with an ILogger interface, we could provide one implementation in our Application, and a completely different one in our tests.  This design technique is known as Programming to Contract.

So, let’s start refactoring our sample program by creating an interface for the Logger:

Code Snippet
  1. public interface ILogger
  2. {
  3.     void LogMessage(string message);
  4. }

And then we can implement that interface in our Logger class:

Code Snippet
  1. public class Logger : ILogger
  2. {
  3.     public void LogMessage(string message)
  4.     {
  5.         Console.WriteLine("Log: " + message);
  6.     }
  7. }

And in our test project we could implement it differently, maybe keep a reference to the last message logged so we can check it in our tests for example:

Code Snippet
  1. public class TestLogger : ILogger
  2. {
  3.     public string LastMessage { get; set; }
  4.  
  5.     public void LogMessage(string message)
  6.     {
  7.         LastMessage = message;
  8.     }
  9. }

Great, so we are now programming to contract and have an interface for our logger.  But how does our Customer class know which concrete implementation of the ILogger to use?  If we keep the following code:

Code Snippet
  1. public Customer()
  2. {
  3.     this.logger = new Logger();
  4. }

We’re no better off that where we were originally, as the Customer class is still using a concrete implementation of the Logger class.  What we need to do instead is to tell which implementation of the ILogger to use.  There’s a few different ways to do this.  The simplest way to do this is to provide the implementation in the constructor, by changing our Customer class to the following:

Code Snippet
  1. public class Customer
  2. {
  3.     private ILogger logger;
  4.  
  5.     public Customer(ILogger logger)
  6.     {
  7.         this.logger = logger;
  8.     }
  9.  
  10.     public void Log()
  11.     {
  12.         logger.LogMessage("This should be logged");
  13.     }
  14. }

Now when we instanciate our Customer in the main program, we pass in the implementation of the ILogger we wish to use at that point, like so:

Code Snippet
  1. // Create a new customer
  2. Customer c = new Customer(new Logger());

And in our tests, we could simply pass in an alternative implementation:

Code Snippet
  1. // Create a new customer with a test logger
  2. Customer c = new Customer(new TestLogger());

This is the simplest form of the next of our TLAs: Dependency Injection (DI) – "a technique for supplying an external dependency (i.e. a reference) to a software component – that is, indicating to a part of a program which other parts it can use" – Wikipedia.

You could also use a Factory / Service Locator pattern, but we’re going to skip past that and jump straight into the last of our TLAs: Inversion of Control (IoC), specifically IoC Containers.  Containers provide a centralised place to manage dependencies.  Think of a container as a dictionary which maps interfaces to implemented types. In other words, it resolves dependencies.

While you can code a fairly simple DependencyResolver class using Generics (see James Kovacs’ article for one such example), there are numerous fully fledged implementations in the .Net world. Some popular options are: StructureMap, Castle Windsor, Microsoft’s Unity, and Ninject.  There are many more out there, some of which are listed on Scott Hanselman’s blog.

The rest of this post will focus on Ninject, as I feel it is the simplest to illustrate the concepts of DI without getting too bogged down.  A subsequent post will revist the example using Unity instead to contrast the differences.  The important thing is to remember the concepts behind these different tools are the same, so don’t get too hung up on which is ‘best’.

Ninject

You can download Ninject from here.

There are two main methods you’ll use with Ninject, Bind and Get.

‘Bind’ is used to map the interfaces to the implemented types, i.e. if I ask for an <IBurger>, give me a <BigMac>.  The binding is usually set up in a module which extends the NinjectModule class.  Binding uses generics to provide a fluent interface: Bind<YourInterface>().To<YourConcreteImplementation>();  In our example program it looks like this:

Code Snippet
  1. public class StandardModule : NinjectModule
  2. {
  3.     public override void Load()
  4.     {
  5.         Bind<ILogger>().To<Logger>();
  6.     }
  7. }

‘Get’ is used to actually retrieve your concrete implementations with all their dependencies ‘injected’ into them.  Get is a method on the Kernel. The ‘Kernel’ is your container, and is usually an instance of the StandardKernel which is instanciated with the Module that you setup your bindings in as above.

Code Snippet
  1. IKernel kernel = new StandardKernel( new StandardModule() );
  2.  
  3. // Get a new customer from our container
  4. Customer c = kernel.Get<Customer>();

So, we set up our Kernel with a reference to our module which is binding any request for an ILogger to our Logger class.  We then ‘Get’ our Customer class from the Kernel.  But hang on, our Customer class isn’t implementing an interface, why do we need to get it using the Kernel?  Couldn’t we just instanciate it as usual?

This threw me a little when I first started using Ninject until I realised that unless you get an object via the Kernel, any dependencies that object has will NOT be injected.

So, that’s why we use kernel.Get to retrieve our Customer.  By using the kernel, it ensures that the dependency the Customer has on ILogger is injected using the binding we setup in our StandardModule.

Instead of using the constructor to do the injection, Ninject also allows us to use the [Inject] attribute to mark up Properties, Methods, and Fields to be injected. See: http://ninject.codeplex.com/wikipage?title=Injection%20Patterns&referringTitle=Dependency%20Injection%20With%20Ninject for more details.

So, we could change our Customer class to the following:

Code Snippet
  1. public class AlternativeCustomer
  2. {
  3.     [Inject]
  4.     public ILogger CustomerLogger {get; set;} // Injected automagically when required by Ninject, providing the class is instantiated via Kernal.Get
  5.  
  6.     public AlternativeCustomer()
  7.     {
  8.         // No longer need to instanciate a logger in the constructor as it is injected when required.
  9.     }
  10.  
  11.     public void Log()
  12.     {
  13.         CustomerLogger.LogMessage("This should also be logged");
  14.     }
  15. }

By marking the CustomerLogger property with the [Inject] attribute, whenever it is required (in the Log method for instance), Ninject will resolve the dependency, in this case mapping our ILogger to our Logger class as defined in the StandardModule.

In our tests then, we could create a different set of mappings by binding our ILogger to a different implementation, like our TestLogger.  Dependency Injection and Mocking go together very well, and there is an extension class for Ninject called Ninject.Moq which allows you to bind directly to a mock, e.g:

Code Snippet
  1. Bind<ILogger>().ToMock<ILogger>();

Hopefully that has helped you to see the power of Dependency Injection. Take a look at the attached solution to see the original tightly coupled application, the decoupled application using constructor injection, and then the same application using Ninject, along with a simple unit test using Ninject.

Sample Solution:

References:

Tame Your Software Dependencies for More Flexible Apps
http://msdn.microsoft.com/en-us/magazine/cc337885.aspx

First Steps into Dependency Injection with Ninject
http://adventuresdotnet.blogspot.com/2009/08/first-steps-into-dependency-injection.html

Ninject website
http://ninject.org

Good video tutorial on Ninject:
http://tekpub.com/view/concepts/0

kick it on DotNetKicks.com

Comment Form

top