Today I was setting up an infrastructure for a new project and I met the same problem I used to met couple times before - configuring injection of CommandDispatcher with decorators in Autofac don’t look well nor it’s easy to read. So I wrote extension methods to simplify future usage of decorators in Autofac.

The Autofac way!

According to Autofac’s documentation the official way to register decorators looks like this:

1
2
3
4
5
6
7
8
9
10
11
builder.RegisterType<CommandDispatcher>()
       .Named<ICommandDispatcher>("baseCommandDispatcher");

builder.RegisterDecorator<ICommandDispatcher>(
	(c, inner) => new TransactionalCommandDispatcher(inner),
	fromKey: "baseCommandDispatcher"
);

var container = builder.Build();

var dispatcher = container.Resolve<ICommandDispatcher>();

It looks very messy and complicates registration of more sophisticated structures. What I especially don’t like is this line:

` (c, inner) => new TransactionalCommandDispatcher(inner),`

If decorator has more dependencies than only decorated type this part of code will look terrible.

How I want to do that?

I wanted to make the registration of many levels of decorators as simple as possible:

1
2
3
4
5
6
7
8
builder.RegisterDecorated<CommandDispatcher, ICommandDispatcher>(
           typeof(TransactionalCommandDispatcherDecorator),
           typeof(LoggingCommandDispatcherDecorator)
       .As<ICommandDispatcher>();

var container = builder.Build();

var dispatcher = container.Resolve<ICommandDispatcher>();

The only thing I need to do is to call method RegisterDecorated<TBase, TInterface> on builder, specify base type that will be decorated and interface of the type (and decorators). As parameters I can put any number of decorators. Decorators will be registered in the order specified in parameters.

And at the end when I’ll call dispatcher.Execute(cmd); I expect this call-stack:

LoggingCommandDispatcherDecorator.Execute
    -> TransactionalCommandDispatcherDecorator.Execute
        -> CommandDispatcher.Execute

How to do that?

Simply, using similar approach that is suggested by Autofac documentation - registration of named types:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public static class BuilderExtensions
{
    public static void RegisterDecorated<TBase, TInterface>(this ContainerBuilder builder, params Type[] decorators)
        where TBase : TInterface
    {
        builder.RegisterDecorated<TBase, TInterface>(typeof(TInterface).Name, decorators);
    }

    public static void RegisterDecorated<TBase, TInterface>(this ContainerBuilder builder, string keyBase, params Type[] decorators)
        where TBase : TInterface
    {
        // ... - argument checking

        var numOfDecorators = decorators.Length;

        builder.RegisterType<TBase>().Named<TInterface>($"{keyBase}-0");

        for (int i = 1; i < numOfDecorators; i++)
        {
            var decorator = decorators[i - 1];

            var currentKey = $"{keyBase}-{i}";
            var previousKey = $"{keyBase}-{i - 1}";

            builder.RegisterType(decorator)
                .WithParameter(
                    (parameterInfo, _) => parameterInfo.ParameterType == typeof(TInterface),
                    (_, context) => context.ResolveNamed<TInterface>(previousKey)
                ).Named<TInterface>(currentKey);
        }

        builder.RegisterType(decorators.Last())
            .WithParameter(
                (parameterInfo, _) => parameterInfo.ParameterType == typeof(TInterface),
                (_, context) => context.ResolveNamed<TInterface>($"{keyBase}-{numOfDecorators - 1}")
            ).As<TInterface>();
    }
}

What is happening? I’m simply registering base type with name: {InterfaceTypeName}-0 (line 16) and following decorators as {InterfaceTypeName-i} (lines 18-30). Last decorator is registered as interface that will requested to be injected (lines 32-36). In this particular case it looks as follows:

Type   Name
CommandDispatcher -> ICommandDispatcher-0
TransactionalCommandDispatcherDecorator -> ICommandDispatcher-1
LoggingCommandDispatcherDecorator    


If for some reason default value of keyBase (name of TInterace) don’t work in your case, you can easily specify other value during registration. In this approach all external dependencies will be resolved as usually at every level of this decorator chain.

The full code is available HERE.