I want to reuse same class more than once in a chain of decorated objects like this.
InstrumentedTargetDecorator -> ResilientTargetDecorator -> InstrumetedTargetDecorator -> Target
Note that InstrumentedTargetDecorator
is used twice here on purpose.
This is how SimpleInjector
syntax looks to get the above wired up
container.Register<ITarget, Target>(Lifestyle.Scoped);
container.RegisterDecorator(
typeof(ITarget), typeof(InstrumentationTargetDecorator), Lifestyle.Scoped);
container.RegisterDecorator(
typeof(ITarget), typeof(ResilienceTargetDecorator), Lifestyle.Scoped);
container.RegisterDecorator(
typeof(ITarget), typeof(InstrumentationTargetDecorator), Lifestyle.Scoped);
If run as is with standard options this will throw producing following warnings
-[Ambiguous Lifestyles] The registration for ITarget (Async Scoped) maps to the same implementation (InstrumentationTargetDecorator) as the registration for ITarget (Scoped) does, but the registration maps to a different lifestyle. This will cause each registration to resolve to a different instance.
-[Ambiguous Lifestyles] The registration for ITarget (Scoped) maps to the same implementation (InstrumentationTargetDecorator) as the registration for ITarget (Async Scoped) does, but the registration maps to a different lifestyle. This will cause each registration to resolve to a different instance.
(2 warning messages reported because there are 2 decorator registrations of InstrumentationTargetDecorator
)
Only way I could actually get it to work is by not running .Verify()
(and setting EnableAutoVerification
to false
). Needless to say this is not a viable option in practice.
There doesn't seem to be any way to suppress this warning for a single decorator registration, because the regular suppress warning playbook doesn't work for decorators i.e. decorator registrations aren't actually retrievable at registration time e.g.
Container.GetRegistration<ITarget>()
produces the original/non-decorated instance (I suspect that's because decorators aren't really resolved until the object graph is built)
If I try to force the graph building
Container.Verify(VerificationOption.VerifyOnly);
var registration = Container.GetRegistration<ITarget>()?.Registration;
registration?.SuppressDiagnosticWarning(DiagnosticType.AmbiguousLifestyles, "I'm right");
Container.Verify();
I can get my hands on the outermost most InstrumentationTargetDecorator
which then allows me to suppress one of the warning messages, but not the other one.
Some more context and the actual program below
Originally I had this setup like this
ResilientTargetDecorator -> Target
(ResilientTargetDecorator wraps Target)
Then I decided to measure how long the Target method call takes, so I introduced a new decorator
ResilientTargetDecorator -> InstrumetedTargetDecorator -> Target
So far so good, this works as expected.
But what if I now want to re-use that instrumentation decorator to also measure how long the entire chain takes including the resilient target decorator ?
Program to reproduce this issue here
You encountered a bug in Simple Injector. I will try to fix this in the next patch release. This bug has been fixed in v5.3.3.
As workaround, when working in an older release, create a derivative of the decorator inside your Composition Root and make that derivative the second registration. Hopefully this suppresses the invalid warning:
class Derivative: InstrumentationTargetDecorator { ...}
container.Register<ITarget, Target>(Lifestyle.Scoped);
container.RegisterDecorator(typeof(ITarget), typeof(InstrumentationTargetDecorator), Lifestyle.Scoped);
container.RegisterDecorator(typeof(ITarget), typeof(ResilienceTargetDecorator), Lifestyle.Scoped);
container.RegisterDecorator(typeof(ITarget), typeof(Derivative), Lifestyle.Scoped);