Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Autofac, how do I replace the default registration with a decorator?

Tags:

c#

autofac

I ran in to some unexpected behaviour in Autofac (v4.4.0, .NET 4.6.1), which I feel may be a bug.

From the documentation:

Default Registrations

If more than one component exposes the same service, Autofac will use the last registered component as the default provider of that service:

e.g. If I do this:

    builder.RegisterType<FooOne>().As<IFoo>();
    // Next line replaces default IFoo implementation
    builder.RegisterType<FooTwo>().As<IFoo>();

I expect (and get) an instance of FooTwo when I resolve IFoo from the container.

However, if I have already registered an IFoo, and I use the RegisterDecorator<T> function to register a decorator of IFoo, it does not replace the default registration of IFoo. e.g:

    // Register FooOne directly
    builder.RegisterType<FooOne>().As<IFoo>(); // (1)
    // Now set up a decorator
    builder.RegisterType<FooOne>().Named<IFoo>("foo");
    builder.RegisterDecorator<IFoo>((c, inner) =>
      new DecoratorFoo(inner), fromKey: "foo");

Will resolve IFoo as an undecorated FooOne. This feels inconsistent to me; a violation of the principle of least astonishment.

You might claim that I shouldn't have line (1) at all (and the code works correctly without it) - but it's an accepted idiom (e.g. here) to use a plug-in to replace registrations.

In my case, this bug manisfested in a fairly large codebase where the wrapped type was already registered by a convention and, now needing to be decorated, requires a significant re-think of how registration is done.

A Gist suitable for LINQPad demonstrating the issue is here.

Is there any design reason for the observed behaviour that I am missing? Is there a way to have the decorator registration replace the default?

like image 979
James World Avatar asked Apr 02 '17 12:04

James World


1 Answers

You can see open issue about your problem here. In discussion there you can find reasoning behind this behavior. They are trying to fix this as I understand, but it turns out it is not quite trivial because of inner workings of autofac.

Meanwhile as a workaround you can register your decorator like this:

builder.Register((cnt, parameters) => new DecoratorFoo(cnt.ResolveNamed<IFoo>("foo", parameters))).As<IFoo>();

This will also fix another issue\bug (mentioned in this stackoverflow question) which you might experience (or not) later, related to the fact decorator will not propagate parameters forward to decorated object.

like image 93
Evk Avatar answered Nov 07 '22 17:11

Evk