Question

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.

Was it helpful?

Solution

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:

  1. 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.

  1. 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()
    
  2. Register a list of Registration instances in one of the RegisterAll overloads, since the Registration object knows about the actual implemenation type.

  3. 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.

OTHER TIPS

Why dont you just check the type of the firstInstance? Wouldn't that give you the actual implementation type? I have to say though that the fact that you need to know the implementation type is a good indication of problems with your abstraction.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top