Wednesday 13 November 2013

Opt in and opt out using Autofac IoC container

I am a big fan of dependency injection and inversion of control. I strive to use these techniques in almost every project I am involved with. Usually when I develop the project from the ground, the whole design is ready for automatic types scanning and auto wiring. So it is very easy to wire them up using a short piece of code. All the examples in this post will be using Autofac - the addictive IoC container.
 
public IContainer Initialize()
{
    var builder = new ContainerBuilder();
    builder.RegisterAssemblyTypes(GetType().Assembly).AsImplementedInterfaces();
    return builder.Build();
}

But sometimes, I need to prevent some classes from being registered, for example, the instance need to be created in run time with specific context parameters. In this case I use the opt-out, i.e. register all the types except those having a custom attribute:
 
[AttributeUsage(AttributeTargets.Class)]
public class DontRegisterInContainerAttribute : Attribute
{

}

public IContainer Initialize()
{
    var builder = new ContainerBuilder();
    builder.RegisterAssemblyTypes(GetType().Assembly)
        .Where(t => !t.HasAttribute<DontRegisterInContainerAttribute>())
        .AsImplementedInterfaces();
    return builder.Build();
}
In order to use the attribute in this syntax, I am using an extension method:
 
public static class TypeExtensions
{
    public static bool HasAttribute<T>(this Type @this)
    {
        return @this.GetCustomAttributes(typeof (T), false).Any();
    }
}

In some projects, when I am modifying existing code, which is not "IoC-ready", I need to register only a few classes. Hopefully their number will increase in the future. But currently what I am actually want is to type the container: register only these classes, without actually listing them or assume they reside in some namespace or have something in their name. In such cases I use the opposite:
 
public IContainer Initialize()
{
    var builder = new ContainerBuilder();
    builder.RegisterAssemblyTypes(GetType().Assembly)
        .Where(t => t.HasAttribute<RegisterInContainerAttribute>())
        .AsImplementedInterfaces();
    return builder.Build();
}

With the corresponding custom attribute:
 
[AttributeUsage(AttributeTargets.Class)]
public class RegisterInContainerAttribute : Attribute
{
         
}

Using this method of registering only types marked with the attribute also makes the code clearer to co-workers, who might look up for usages of the attribute and find out what classes are registered in the container. In case they are not familiar with Agent Mulder Resharper plugin.