Sunday, December 28, 2008

5-minute dependency injection container in C#

I found Misko Hevery's talks on the usefulness of Dependency Injection to be quite persuasive, so I have started using constructor DI in my projects. Unfortunately, some of those involve ASP.NET WebForms applications, and even in the one I just started using ASP.NET MVC there are classes I do not create myself (like the controllers). This means that when I write something like

  1. public class ProductController  
  2. {  
  3.   private ProductRepository repository;  
  5.   public ProductController()  
  6.   {  
  7.     repository = new ProductRepository();  
  8.   }  
  9. }  

I am hard-coding a dependency on the concrete ProductRepository class inside ProductController.

There are (at least) two solutions to this. One of them would be to create a property in the ProductController class:

  1. public IProductRepository Repository { getset; }  

and change the constructor to

  1. public ProductController()  
  2. {  
  3.   Repository = new ProductRepository();  
  4. }  

This is the usual property dependency injection, and it works quite well. However, I find it annoying to do this for every class, not to mention that I don't like the idea of adding a public property just so I can test the class, since this breaks the information hiding tenet.

A variant of this option is to have two constructors:

  1. public ProductController(): this(new ProductRepository())  
  2. {  
  3. }  
  5. public ProductController(IProductRepository repository)  
  6. {  
  7.   Repository = repository;  
  8. }  

A second option would be to use a DI container and do this:

  1. public ProductController()  
  2. {  
  3.   repository = Container.Get<IProductRepository>();  
  4. }  

and somehow instruct the program, in the initialization part (like Global.asax.cs), to return a new ProductRepository instance every time I ask for an IProductRepository.

This seems to me a bit more fluent, however the association part in (most of) the existing DI containers seem to be overkill.

So... I came up with a home-made DI container by using the pattern in my previous post:

  1. public class GenericContainer  
  2. {  
  3.   private static readonly Dictionary<Type, Func<object>> container =  
  4.     new Dictionary<Type, Func<object>>();  
  6.   public static void Register<T>(Func<T> func) where T : class  
  7.   {  
  8.     container.Add(typeof (T), () => func());  
  9.   }  
  11.   public static void Unregister<T>() where T : class  
  12.   {  
  13.     container.Remove(typeof (T));  
  14.   }  
  16.   public static T Get<T>() where T : class  
  17.   {  
  18.     return container[typeof (T)]() as T;  
  19.   }  
  20. }  

(The Register() method uses a trick here to convert a Func<T> to a Func<object> so that it can be saved in the dictionary - I should be able to get rid of this once we get covariance / contravariance in .NET 4.0 and Visual Studio 2010.)

The usage is quite simple - this call will associate a type with a function returning instances of that type:

  1. GenericContainer.Register<IProductRepository>(() => new ProductRepository());  

and this call will replace the "new ProductRepository()" call:

  1. GenericContainer.Get<IProductRepository>();  

(Of course, I don't have to use interfaces here; I can use any type.)

Note that the flexibility of the function allows me to also handle the use cases where I want a singleton returned:

  1. var productRepository = new ProductRepository();  
  2. GenericContainer.Register<IProductRepository>(() => productRepository);  

During testing I will want to replace the object being returned with a mock object I control:

  1. GenericContainer.Register<IProductRepository>(() => new MockProductRepository());  

It's that simple!

As you can see, I'm quite excited about this... it might not have all the bells and whistles of a mainstream DI container, but I find it quite good for what I need.

Edit: Ok, this might be more of a Service Locator pattern instead of Dependency Injection... I am unclear on the terminology. Useful in any case, though :)

Edit 2: It looks like this pattern does have a problem: it hides dependencies. The constructor dependency injection shows clearly that class A depends on classes B, C, and D. Using a service locator hides that. I think the compromise suggested in the article - create two constructors, one exposing the dependencies and one using the service locator - is the best way for now.

No comments: