One of my favorite design pattern is the Decorator pattern. It's an elegant way to compose behavior without changing existing code. It's non-intrusive and makes the separation of concerns very clear between each class. Everything is then very easy to test and reuse.

So what's the problem?

The pain point I always have with this pattern is to create a long chain of decorator in a clean and simple way. It always ends up looking like this or even worst:

new LoggerDecorator(new ErrorHandlerDecorator(new CacheDecorator(new MyService())));

Moreover, this needs to be repeated every time I need MyService.

Solutions!

Hopefully, there are some solutions out there to tackle this specific problem. One of them is to use a DI framework like Autofac. Autofac already provides out of the box a nice construct to register decorators in your container and will make sure to resolve your decorator chain at runtime.

Extract from Autofac's documentation

var builder = new ContainerBuilder();

// Register the services to be decorated. You have to
// name them rather than register them As<ICommandHandler>()
// so the *decorator* can be the As<ICommandHandler>() registration.
builder.RegisterType<SaveCommandHandler>()
       .Named<ICommandHandler>("handler");
builder.RegisterType<OpenCommandHandler>()
       .Named<ICommandHandler>("handler");

// Then register the decorator. The decorator uses the
// named registrations to get the items to wrap.
builder.RegisterDecorator<ICommandHandler>(
    (c, inner) => new CommandHandlerDecorator(inner),
    fromKey: "handler");

var container = builder.Build();

// The resolved set of commands will have two items
// in it, both of which will be wrapped in a CommandHandlerDecorator.
var handlers = container.Resolve<IEnumerable<ICommandHandler>>();

Well... this is a bit better as we only have to register our chain of decorator once. However, the registration part is still hard to understand. Nesting a few builder.RegisterDecorator gets complex real fast.

The simplified approach

What I would like is a simple interface to get rid of most of the boiler plate Autofac code lines required for the builder.RegisterDecorator.
Something like this

builder.RegisterTypeWithDecorator<IMyService, MyService>()
                .WithDecorator<CacheDecorator>()
                .WithDecorator<ErrorHandlerDecorator>()
                .WithDecorator<LoggerDecorator>()
                .CompleteDecoratorRegistration();

Haaaaa, it feels much better and cleaner. Now let's try to implement this.

NOTE: The next part will be complicated, but it's ok. The goal is to abstract that complexity away, so we can use the nice methods above.

Interface

public interface IRegistrationDecoratorBuilder<TInterface, TImplementation>
{
    IRegistrationDecoratorBuilder<TInterface, TImplementation> WithDecorator<TDecorator>()
        where TDecorator : TInterface;

    // The return type is quite ugly, but expected by Autofac
    IRegistrationBuilder<TInterface, LightweightAdapterActivatorData, DynamicRegistrationStyle> CompleteDecoratorRegistration();
}

Implementation

internal class RegistrationDecoratorBuilder<TInterface, TImplementation>
        : IRegistrationDecoratorBuilder<TInterface, TImplementation>
{
    private readonly ContainerBuilder _builder;
    private readonly IList<Type> _decoratorTypes = new List<Type>();

    public RegistrationDecoratorBuilder(ContainerBuilder builder)
    {
        this._builder = builder;
    }

    public IRegistrationDecoratorBuilder<TInterface, TImplementation> WithDecorator<TDecorator>()
        where TDecorator : TInterface
    {
        this._decoratorTypes.Add(typeof(TDecorator));

        return this;
    }

    public IRegistrationBuilder<TInterface, LightweightAdapterActivatorData, DynamicRegistrationStyle>
        CompleteDecoratorRegistration()
    {
        var registrationNameToDecorate = this.RegisterConcreteImplementationToDecorate();
        registrationNameToDecorate = this.GetDecoratorTypeExceptTheTopMost()
            .Aggregate(
                registrationNameToDecorate,
                (current, decoratorType) => this.RegisterDecorator(decoratorType, current));

        return this.RegisterTopMostDecorator(registrationNameToDecorate);
    }

    private IEnumerable<Type> GetDecoratorTypeExceptTheTopMost()
    {
        return this._decoratorTypes.Take(this._decoratorTypes.Count - 1);
    }

    private string RegisterConcreteImplementationToDecorate()
    {
        var registrationNameToDecorate = nameof(TImplementation);
        this._builder.RegisterType<TImplementation>().Named<TInterface>(registrationNameToDecorate);
        return registrationNameToDecorate;
    }

    private string RegisterDecorator(Type decoratorType, string registrationNameToDecorate)
    {
        var decoratorTypeName = decoratorType.Name;
        this._builder.RegisterType(decoratorType).Keyed(decoratorTypeName, decoratorType);
        this._builder.RegisterDecorator<TInterface>(
            (context, inner) => (TInterface)context.ResolveKeyed(
                decoratorTypeName,
                decoratorType,
                new TypedParameter(typeof(TInterface), inner)),
            registrationNameToDecorate,
            decoratorTypeName);
        registrationNameToDecorate = decoratorTypeName;
        return registrationNameToDecorate;
    }

    private IRegistrationBuilder<TInterface, LightweightAdapterActivatorData, DynamicRegistrationStyle>
        RegisterTopMostDecorator(string registrationNameToDecorate)
    {
        var lastDecoratorType = this._decoratorTypes.Last();
        this._builder.RegisterType(lastDecoratorType).Keyed(lastDecoratorType.Name, lastDecoratorType);
        return this._builder.RegisterDecorator<TInterface>(
            (context, inner) => (TInterface)context.ResolveKeyed(
                lastDecoratorType.Name,
                lastDecoratorType,
                new TypedParameter(typeof(TInterface), inner)),
            registrationNameToDecorate);
    }

Extension method

public static IRegistrationDecoratorBuilder<TInterface, TImplementation> RegisterTypeWithDecorator<TInterface,
        TImplementation>(this ContainerBuilder builder)
        where TImplementation : TInterface
{
    return new RegistrationDecoratorBuilder<TInterface, TImplementation>(builder);
}

Conclusion

Voilà! We abstracted all the complexity away with the magic of c# extension methods, so our new decorator builder adds up seamlessly to the Autofac container builder.