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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With