I think everyone knows about Dependency Injection (DI) and how important it is when it comes to building low-coupled components. There are many IoC (Inversion of Control) containers. The most popular are
Each of them has its own pros and cons. I have been using NInject for years and I am really happy with it. Autofac is quite a new DI framework which shows really good results on benchmarks. You can find information for all DI frameworks on their websites or just google them for comparison.
Common Service Locator (CSL)
As there are many DI frameworks you are not safe. You may start using one of them in the beginning and then decide to change it (because of performance reasons for example). Even though all DI frameworks have something similar between each other, it needs time to replace a IoC container. That’s why Common Service Locator is introduced. It is a library which contains a common interface for working with most of the IoC containers. This way you actually do not rely directly on a IoC container. If you need to replace it at a time you can easily do it without modifying your project.
How does it work?
Actually it’s pretty simple. Every IoC container provides the bidings (interface to class, etc.) and CSL provides a common interface to request objects. In the beginning you set up the bindings and tell CSL how to access them. The rest is quite straightforward. Ninject 2 already supports CSL by providing a special factory to access Ninject’s kernel. The factory resides in CommonServiceLocator.Ninject.dll. Let’s have a look at the following example. I need to inject my repository into my controller.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public interface IRepository { IEnumerable GetItems(); } public class MyRepository : IRepository { ... } public class HomeController : Controller { private readonly IRepository repository; public HomeController(IRepository repository) { this.repository = repository; } public ActionResult Index() { var items = this.repository.GetItems(); return View(items); } } |
The first step is to provide CSL access to Ninject’s kernel. This is done by registering Ninject’s service provider (which contains all bindings) with CSL.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class MvcApplication : HttpApplication { protected void Application_Start() { ... ServiceLocator.SetLocatorProvider(() => new NinjectServiceLocator(CreateKernel())); ... } private static IKernel CreateKernel() { IKernel kernel = new StandardKernel(); kernel.Bind(typeof(IRepository)).To(typeof(MyRepository)); return kernel; } } |
Now you can request objects by using CSL API – ServiceLocator.Current.GetInstance<IRepository>(). However you should tell the ASP.NET MVC engine how to resolve the controllers for you. Otherwise you will get an exception when you load the page. One possible solution is to create another parameterless contructor for your controller which calls the second one by using CSL API to provide a repository instance. However I prefer a far better approach. Instead of taking care for this, you can create a custom controller factory to create controllers for you automatically.
1 2 3 4 5 6 7 8 9 10 |
public class CommonServiceLocatorControllerFactory : DefaultControllerFactory { protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType == null) return base.GetControllerInstance(requestContext, controllerType); return ServiceLocator.Current.GetInstance(controllerType) as IController; } } |
You inherit the default controller factory and override its GetControllerInstance which is responsible for creating a controller instance. The only thing left is to register your controller factory within the ASP.NET MVC engine.
1 |
ControllerBuilder.Current.SetControllerFactory(typeof(CommonServiceLocatorControllerFactory)); |
That’s it! You have fully automated controller creation which uses dependency injection to communicate with lower layers in your system. Even if you decide to change your IoC container you only change the initialization step and the rest remains the same.