Project Injection

Application of Property Injection

Dependency Injection DI Wednesday, May 21, 2014

Property injection is the dependency injection pattern that consists of creating a property publicly settable (so that it can be set e.g. as a stub in a unit test) but with a good local default that makes it optional to set.

An example of this would be a profile service that relies on the HttpContext. Let's imagine the following scenario:


public interface IProfileService
{
    Language GetCurrentLanguage();
}

public class DefaultProfileService : IProfileService
{
    private readonly HttpContext context;

    public DefaultProfileService(HttpContext context)
    {
        this.context = context;
    }

    public Language GetCurrentLanguage()
    {
        // implementation
    }
}

As you can see, the DefaultProfileService contains a dependency of type HttpContext which is not available when executing the constructor even if you try HttpContext.Current. That's why Castle Windsor throws the following exception if we try to ask for an IProfileService in the controller constructor:

Can't create component 'Bomboca.DIExample.Services.Implementations.DefaultProfileService' as it has dependencies to be satisfied.

'Bomboca.DIExample.Services.Implementations.DefaultProfileService' is waiting for the following dependencies:
- Service 'System.Web.HttpContext' which was not registered.

Well, considering what we stated previously about a dependency that is a good candidate for property injection: having a good default, making it optional and, ultimately, the dependency is not available in the constructor, then this is a case in which we can apply the property injection dependency injection pattern. The following should do:

private IProfileService profileService;

public IProfileService ProfileService
{
    get
    {
        if (this.profileService == null)
        {
            this.profileService = new DefaultProfileService(System.Web.HttpContext.Current);
        }

        return profileService;
    }

    set
    {
        if (value == null)
        {
            throw new ArgumentNullException("value");
        }

        if (this.profileService != null)
        {
            throw new InvalidOperationException("Profile service is already set.");
        }

        this.profileService = value;
    }
}

Notice that the IProfileService field is null until it is set or until it is read. It cannot be changed to null and it cannot be assigned a second as to avoid inconsistent behaviour.

Alternatives to Property Injection

There's an alternative to property injection that we can use here though. But didn't you say that the HttpContext is not available? Yes. So what?

IoC Container Factory Method

It is true that the HttpContext is not available in a controller constructor but if you would go back to using constructor injection to assign the IProfileService private field (this would bring you back the error), you could register the HttpContext type in your container and, instead of specifying a type for the IoC container to instantiate, you specify a factory method which is nothing more than a lambda expression that returns the instance you want. In the case of the HttpContext, that's just calling the Current static property:

public class ContextsInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Kernel.Register(
            Component.For()
            .UsingFactoryMethod(() => HttpContext.Current));
    }
}

Using an abstract factory injected in the constructor would allow you to serve a similar result but at its core that is similar to what Castle Windsor allow you to do here. Other IoC containers provide similar capabilities in this respect.

Other Alternative

I forgot the other one.