Friday, 29 May 2009

Avoiding static references to an IoC container

It seems quite common for applications to employ a static class to encapsulate the Inversion of Control container. A simple example of such a class might be:

public static class IoC
{
    private static IWindsorContainer _container = new WindsorContainer();

    public static T Resolve<T>()
    {
        return _container.Resolve<T>();
    }
}

While the normal practise would be to inject dependencies through the constructor, there are times where you may need to pass extra arguments as part of the Resolve() method. You might end up with a method such as this:

    public class SomeClass : ISomeClass
    {
        public SomeClass()
        { }

        public void SomeMethod(string name)
        {
            if (name == "david")
            {
                var class1 = IoC.Resolve<IClass1>();
            }
            else
            {
                var class2 = IoC.Resolve<IClass2>();
            }
        }
    }

This works, but because of the tight coupling to the IoC class, it isn’t ideal. It also makes it harder to test as you are going to have to ensure that WindsorContainer gets configured appropriately.

A better solution is to add a dependency in the contructor for IKernel. If this class is resolved via the container, then it will resolve IKernel to a reference of the current Windsor MicroKernel object (which WindsorContainer inherits from). You can then use the reference to kernel to call its Resolve() method. Having the kernel injected now means it is now elementary to pass in a mock in your test code, mitigating the need to have all your castle.config configuration for your unit tests.

public class SomeClass : ISomeClass
{
    private IKernel _kernel;
    public SomeClass(IKernel kernel)
    { 
        _kernel = kernel;
    }

    public void SomeMethod(string name)
    {
        if (name == "david")
        {
            var class1 = _kernel.Resolve<IClass1>();
        }
        else
        {
            var class2 = _kernel.Resolve<IClass2>();
        }
    }
}

2 comments:

David Gardiner said...

Was trying to figure out how this worked and discovered my own blog post about it :-)

Curiously, calling container.Resolve() doesn't work whereas injecting IKernel via the constructor does.

-dave

Anonymous said...

funny, but that's why you should post blog posts, so you can check later how it works :)