I have this code:
using (container.BeginLifetimeScope())
{
RenderWord instruction = new RenderWord();
var instances = container.GetAllInstances<IInstructionHandler<RenderWord>>();
var firstInstance = result.First();
}
instances
is of type IEnumerable<IInstructionHandler<RenderWord>>
firstInstance
is of type IInstructionHandler<RenderWord>
that in reality is an instance of a decorator that decorates another decorator that decorates another decorator ...
At runtime the actual class instances
is of type ContainerControlledCollection<IInstructionHandler<RenderWord>>
and this ContainerControlledCollection
class holds a very useful piece of information - the underlying ImplementationType
.
Is there any way for me to get to the ContainerControlledCollection
or the producers[0].Value.ImplementationType
at runtime because I’d really like to be able to discover the base implementation type underneath the chain of decorators.
I think @atomaras might have a good point about your abstraction, although I think it would be fine when you only use this information inside your Composition Root, since your Composition Root is already aware of every implementation in the system.
I think there are a few ways to get to this information:
Use the DecoratorPredicateContext
information that is supplied to the RegisterDecorator
extension method:
var typeMapping = new Dictionary<Type, Type>();
container.RegisterDecorator(typeof(IInstructionHandler<>), typeof(FakeDecorator<>), c =>
{
typeMapping[c.ServiceType] = c.ImplementationType;
// or perhaps even use c.AppliedDecorators property to see which decorators
// are applied.
// return false to prevent the decorator from being applied.
return false;
});
You can make a fake registration that Simple Injector will call for every IInstructionHandler<T>
in the system, but you prevent it from being applied by supplying a predicate that will always return false
. You can use the info supplied by Simple Injector in the DecoratorPredicateContext
to find out what the actual ImplementationType is.
Alternatively, you can inject an DecoratorContext
instance (v2.6 and up) into the top most decorator (as explained here). The DecoratorContext
contains the same information as the DecoratorPredicateContext
does, but this object will automatically be injected by Simple Injector into a decorator that depends on. It allows you to make the decision inside a decorator, which might be very convenient in your case.
Add an an IDecorator
abstraction to the system to allow traversing the decorator chain.
By letting each decorator implement a IDecorator
interface that allows access to the decoratee (just as done here) you can traverse the decorator chain and find the actual implementation type:
public interface IDecorator
{
object Decoratee { get; }
}
public static class DecoratorHelpers
{
public static IEnumerable<object> GetDecoratorChain(IDecorator decorator)
{
while (decorator != null)
{
yield return decorator;
decorator = decorator.Decoratee as IDecorator;
}
}
}
You can implement your decorators with this interface as follows:
public class SomeDecorator<T> : IInstructionHandler<T>, IDecorator
{
private readonly IInstructionHandler<T> decoratee;
public SomeDecorator(IInstructionHandler<T> decoratee)
{
this.decoratee = decoratee;
}
object IDecorator.Decoratee { get { return this.decoratee; } }
}
When you implemented this interface on all your decorators, you will be able to do this:
var implementationTypes =
from handler in container.GetAllInstances<IInstructionHandler<RenderWord>>()
let mostInnerDecorator =
DecoratorHelpers.GetDecoratorChain(handler as IDecorator).LastOrDefault()
let implementation = mostInnerDecorator != null ? mostInnerDecorator.Decoratee : handler
select implementation.GetType()
Register a list of Registration
instances in one of the RegisterAll
overloads, since the Registration
object knows about the actual implemenation type.
But instead of point 3, you might as well use the list of implemenation types that you used to create those registrations:
typeMapping[serviceType] = implementationTypes;
container.RegisterAll(serviceType, implementationTypes);
Simple Injector will resolve the registered implementations always in the same order as they are registered (this is guaranteed). So when you resolve a collection of things, you will already have the list of implementations that is layed out in the same order.