I have a scenario where I have two classes, let's call them SingletonWorker
and ScopedWorker
, registered with Singleton and Scoped lifestyles respectively. Both depend on IMetricSubmitter
in their constructor. The workers submit metrics as part of their logic using the IMetricSubmitter
. There's a singleton implementation for IMetricSubmitter
called DefaultMetricSubmitter
, which I like to decorate with EnrichMetricsDecorator
for the purpose of ScopedWorker
dependency, such that SingletonWorker
will end up with DefaultMetricSubmitter
and ScopedWorker
will end up with EnrichMetricsDecorator
decorating DefaultMetricSubmitter
. Is there a way to create such registrations using SimpleInjector today?
The object graphs should basically look like this:
var singleton = new SingletonWorker(
new DefaultMetricSubmitter());
var scoped = new ScopedWorker(
new EnrichMetricsDecorator(
new DefaultMetricSubmitter()));
To me it looks like combining the RegisterConditional
, which has a predicate that knows about the consumer, with RegisterDecorator
which is the way to register decorators, but I don't know of any way to combine the two. Ideally I'd like to register the decorator with a condition that is based on whether there's an active scope when it's requested as a dependency for a consuming constructor, and in that case create the decorator instance for that active scope. For the purpose of debate, the scope can be assumed to be LifetimeScope
.
What you want to do is not possible using the RegisterDecorator
methods. Instead, you will have to revert to using the RegisterConditional
methods. Considering your given object graph, these registrations should look something like this:
container.Register<ScopedWorker>(Lifestyle.Scoped);
container.Register<SingletonWorker>(Lifestyle.Singleton);
container.RegisterConditional<IMetricSubmitter, EnrichMetricsDecorator>(
Lifestyle.Scoped,
c => c.Consumer.ImplementationType == typeof(ScopedWorker));
container.RegisterConditional<IMetricSubmitter, DefaultMetricSubmitter>(
Lifestyle.Singleton,
c => c.Consumer.ImplementationType == typeof(EnrichMetricsDecorator));
This practice is described here in the documentation.
UPDATE
With your updated object graph (that includes an extra singleton decorator), the registration might look as follows:
// Useful helper method
static bool InjectedInto<TConsumer>(PredicateContext c) =>
c.Consumer.ImplementationType == typeof(TConsumer);
container.Register<ScopedWorker>(Lifestyle.Scoped);
container.Register<SingletonWorker>(Lifestyle.Singleton);
container.RegisterConditional<IMetricSubmitter, DefaultMetricSubmitter>(
Lifestyle.Singleton,
InjectedInto<CachingMetricSubmitterDecorator>);
container.RegisterConditional<IMetricSubmitter, CachingMetricSubmitterDecorator>(
Lifestyle.Singleton,
c=> !InjectedInto<ScopedWorker>(c)&&!InjectedInto<CachingMetricSubmitterDecorator>(c));
container.RegisterConditional<IMetricSubmitter, EnrichMetricsDecorator>(
Lifestyle.Scoped,
InjectedInto<ScopedWorker>);