A step-by-step guide to using Ninject for dependency injection in C#
Recently, I’ve been trying out the Ninject Dependency Injection framework. It’s amazing! In this post I would like to give you some pointers on how to use it. I will also briefly explain the purpose of Dependency Injection. For those aching to play with some code rather than reading a lengthy blog (for webforms or as a console application), check the sourcecode repositories below.
What is dependency injection?
Dependency Injection (DI, wikipedia) is a design pattern that reduces hard-coded dependencies between your classes by injecting these dependencies at run-time, instead of during design-time. Technically, Dependency Injection is a mechanism that allows the implementation of another, more high-level, design pattern called Inversion of Control (IoC, wikipedia). The purpose of both patterns is to reduce hard-coded dependencies (or ‘coupling’) between your classes.
What are dependencies?
Suppose that you are building a web application that is going to send e-mails to visitors that have entered a form. In object-oriented code, it is important to separate responsibilities. So you’ll probably end up with a class that handles the form input (FormHandler) and a class that is responsible for sending the e-mails (MailSender). The MailHandler class looks like this:
public class MailSender
{
public void Send(string toAddress, string subject)
{
Console.WriteLine("Sending mail to [{0}] with subject [{1}]", toAddress, subject);
}
}
If you don’t use Inversion of Control, your FormHandler class will look like this:
public class FormHandler
{
public void Handle(string toAddress)
{
MailSender mailSender = new MailSender();
mailSender.Send(toAddress, "This is non-Ninject example");
}
}
Although there’s nothing wrong with this code, it is creating a dependency between the FormHandler and MailSender classes. The dependency is created at line 5. Using the New keyword in your code to instantiate a class implies that you are creating a dependency. From a practical point of view, you are telling your FormHandler class to use a concrete implementation of the MailSender class. There’s no flexibility. ‘But why’, you ask, ‘is this a bad thing?’. Let’s take a look at some reasons …
Why are dependencies a bad thing?
- You can’t use multiple implementations of the MailSender class: If you write the code like shown above, you will lose one of the benefits of object oriented code. You can’t easily swap out the implementation of MailSender with another implementation. Perhaps you want to avoid sending real mails and log them instead for a staging environment of your application. Or you want to send mails in plaintext instead of with HTML. In these cases, you can only change the implementation of MailSender by changing the MailSender class and the FormHandler classes. Bottom-line: you lose flexibility;
- It makes it easier to write sloppy code: If your classes are tightly coupled, it will be more tempting to mix up responsibilities. If you use Dependency Injection (as you’ll see), you have to invest more time in coming up with a good design for your classes and, specifically, their interface. Therefore, using Inversion of Control or Dependency Injection will improve the quality of code because it makes it harder to cut corners;
- It makes unit testing (nearly) impossible: When you’re writing unit tests for a class, you want to test only the behavior of that particular class. If you would write unit tests for the FormHandler, you’ll end up testing the MailSender as well. After all, the MailSender is used ‘under the hood’ of the Handle method, and there’s no way to do anything about that. This makes writing unit tests nearly impossible. If you are writing unit tests, dependency injection is often required;
The bottom-line is that your FormHandler shouldn’t know what concrete implementation of the MailSender is used. What concrete implementation is used, should be determined outside of your classes. That way, you can swap out the MailSender with another implementation if the need arises. If you are writing unit tests for the FormHandler, you can swap out the MailSender class with a mocked version, for example.
So, how do I get rid of dependencies?
One way to do this is by using a Dependency Injection framework, like Spring, Unityor Ninject. These frameworks allow you to configure, separate from your classes, which concrete implementations should be used. I prefer Ninject because it is lightweight, easy to use and requires little change in your code.
Step 1: Download Ninject
Go to the Ninject website and download the latest version for the .NET platform you’re targeting. Take .NET 4.5 if you are not sure. You can do this through NuGet in Visual Studio. I still prefer the manual approach and put the assembly (ninject.dll) in a folder in my solution called \Assemblies.
Step 2: Preparing the code
Before diving into Ninject, the first thing we have to do is rewrite our code to use Interfaces. This is required for Dependency Injection, but is good practice anyways. An interfaces is basically a contract between classes that force one class to behave exactly as described by the interface. The contract can be implemented by other implementations (different class, same behavior). We create a very straightforward interface for the MailSender class:
public interface IMailSender
{
void Send(string toAddress, string subject);
}
We also rewrite the MailSender class to implement the IMailSender interface:
public class MailSender : IMailSender
{
public void Send(string toAddress, string subject)
{
Console.WriteLine("Sending mail to [{0}] with subject [{1}]", toAddress, subject);
}
}
We’ve basically told C# that the concrete implementation of our MailSender class follows the IMailSender interface (or contract). It is now possible to create other concrete implementations that follow the same interface but do different things ‘under the hood’, for example:
public class MockMailSender : IMailSender
{
public void Send(string toAddress, string subject)
{
Console.WriteLine("Mocking mail to [{0}] with subject [{1}]", toAddress, subject);
}
}
Right now, we can change which concrete implementation is used by our FormHandler by rewriting the code where the class is instantiated:
public class FormHandler
{
public void Handle(string toAddress)
{
IMailSender mailSender = new MockMailSender();
mailSender.Send(toAddress, "This is still a non-Ninject example");
}
}
Of course, this already adds a lot of flexibility to our code, as we can swap out which concrete implementation of IMailService is used by changing line 5. Although C# will now force you to implement the IMailSender interface to the letter, your code will already improve a lot by using interfaces. The next step is to implement manual dependency injection to get rid of this codechange that is required to change which implementation is used.
Step 3: Implementing manual dependency injection
Before using Ninject to inject dependencies, it’s useful to do it manually to understand the basics. Basically, we’re going to pass the dependency in through the constructorof the FormHandler class. This way, the code that is using the FormHandler can determine which concrete implementation of IMailSender to use:
public class FormHandler
{
private readonly IMailSender mailSender;
public FormHandler(IMailSender mailSender)
{
this.mailSender = mailSender;
}
public void Handle(string toAddress)
{
mailSender.Send(toAddress, "This is non-Ninject example");
}
The code that creates our FormHandler has to pass in a concrete implementation of IMailService. This means that control is now inverted; instead of the FormHandler deciding which implementation to use, the calling code does. This is the whole point of Inversion of Control, of which Dependency Injection is just one approach. The calling code (the code that uses the FormHandler and now controls the dependencies) looks like this:
class Program
{
static void Main(string[] args)
{
IMailSender mailSender = new MockMailSender();
FormHandler formHandler = new FormHandler(mailSender);
formHandler.Handle("test@test.com");
Console.ReadLine();
}
}
This is an example of manual dependency injection, because we’re not relying on any framework to do the heavy lifting for us. The above code is fine, and will work like a charm. But this approach will become progressively harder as the number of dependencies increases. After all, you’ll have to add one constructor parameter for every new dependency in the class you’re instantiating. This can be quite infuriating. Therefore, we need a framework to take care of this. This is where Ninject, or any other DI framework, comes in.
Step 4: Implementing Ninject to inject dependencies for us
Ninject is fairly extensive, but I’ll stick to the easiest (and most often used) dependency injection, called constructor injection. The nice thing about Ninject is that you don’t have to change MailSender, IMailSender or FormHandler at all. You do need to add a reference to the Ninject.dll assembly in your project and create a separate class in your project that Ninject uses to configure the dependencies at run-time:
using Ninject.Modules;
using Ninject;
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<IMailSender>().To<MockMailSender>();
}
}
The name of the class can be whatever you like; Ninject will find it as long as it inherits from NinjectModule. Your calling code (Program.cs) has to use Ninjectto determine which concrete implementation to use:
using Ninject;
class Program
{
static void Main(string[] args)
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
var mailSender = kernel.Get<IMailSender>();
var formHandler = new FormHandler(mailSender);
formHandler.Handle("test@test.com");
Console.ReadLine();
}
}
When running this code, your console will say ‘Mocking mail to ….’, which is also what we expected. The dependency injection is working! The code is creating a Ninject Kernel that resolves our entire chain of dependencies. We tell Ninject to load the bindings from the executing assembly. This is the most common scenario. In this case, your Bindings class should live in one of the assemblies included in your executing project. Practically speaking, this means that your Bindings class will usually live in your website, webservice, windows service, console application or unit test project, as they are at the top of the chain of executing code. For every chain / context (website, unit tests, console) you can create a different Bindings class with different configurations. For example, you can change the Bindings class to use the MailSender wherever IMailSender is used:
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<IMailSender>().To<MailSender>();
}
}
Running the same code will now result in your console saying ‘Sending mail to …’ (and not the Mock version), which is what we expected.
Step 5: More levels of dependencies, and where the magic truly shows
The above example works, but it doesn’t show the true power of Ninject. When your project grows, and the number of dependencies increases, Ninject will automatically figure out which concrete implementations to pass into constructors based on the Bindings. Suppose our MailSender is going to call a separate class for logging exceptions. Without dependency injection, our FormHandler would now depend on the MailSender, which in turn depends on the Logging class. So, your MailSender class could look like this:
public class MailSender : IMailSender
{
private readonly ILogging logging;
public MailSender(ILogging logging)
{
this.logging = logging;
}
public void Send(string toAddress, string subject)
{
logging.Debug("Sending mail");
Console.WriteLine(string.Format("Sending mail to [{0}] with subject [{1}]", toAddress, subject));
}
}
Your bindings will look like this:
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<IMailSender>().To<MockMailSender>();
Bind<ILogging>().To<MockLogging>();
}
}
Now, if you call FormHandler from the console application, two dependency injections will take place. The first one we’ve already seen; we ask Ninject to give us a concrete implementation of IMailService and pass it into the constructor of FormHandler. When Ninject instantiates MailSender, it understands that this class requires ILogging. It will check its Bindings and load the concrete implementation specified there automatically. The nice thing is that if you forgot to add a configuration for ILogging in the bindings, Ninject will throw a friendly exception explaining what you have to do.
So, once you’ve set up the top level of the execution hierarchy (in this example) by creating the Kernel, Ninject will take care of the rest for you. This is a big difference from most other Dependency Injection frameworks. They often require code changes within all your classes with dependencies.
Avoid Service Locator anti-patterns
A mistake I initially made is that I used a container or IoC manager as a Service Locator (another design pattern). Basically, I created a class that lived as a singleton and was called from all classes with dependencies to resolve the dependency (IoCManager.Resolve()). Many Dependency Injection frameworks facilitate this approach, like Unity. This is a common strategy, but it causes a dependency on the IoC container _itself_and is not necessary. The code I’ve shown above works without any kind of custom Service Locator pattern. In fact, the MailSender and FormHandler classes have not changed since the manual injection approach. For more information, see this or this blog.
Concluding thoughts
Dependency Injection is a difficult concept to grasp if you’ve never used it before. Just give it a try, and you’ll see how flexible it makes your code. Especially when you’re writing unit tests you’ll quickly see the benefits. In that case, you can easily swap in mock implementations of dependencies. If anything, it makes your code a lot cleaner and sort of forces you to write better code. And don’t forget to check out Ninject’s website for far more advanced scenarios.
Check out the code for a simple console app here (Visual Studio 2013):
https://bitbucket.org/cverwijs/examples.ninject
Or this code for a simple webapplication (Visual Studio 2013):
https://bitbucket.org/cverwijs/examples.ninject.webapp
The webapplication implements the same classes in a Webforms context. Although there is no visible functionality injected by Ninject, you can verify and follow the injection by placing a breakpoint in Default.aspx.cs. Injection is also possible for master pages and other pages, but check the Ninject.Web documentation for that. The bindings are configured in the /App_start/NinjectWebCommon.cs.