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.


No comments: