I asked the question “How to discover the underlying Implementation Type of a decorated instance when calling GetAllInstances?” on stackoverflow and got a perfectly valid answer – here’s a sample implementation to prove that it would work.
Start with some test abstractions and implementations:
public interface ICommandHandler<TCommand>
{
void Execute();
}
public class A { }
public class B { }
public class CommandHandler<TCommand> :
ICommandHandler<TCommand>
{
public void Execute() { }
}
public class CommandHandlerA<TCommand> :
ICommandHandler<TCommand> where TCommand : A
{
public void Execute() { }
}
public class CommandHandlerB<TCommand> :
ICommandHandler<TCommand> where TCommand : B
{
public void Execute() { }
}
For this example I have defined a decorator that will disable the call to the decorated type:
public class DisableCommandHandlerDecorator<TCommand> :
ICommandHandler<TCommand>
{
public DisableCommandHandlerDecorator(
ICommandHandler<TCommand> decorated)
{ }
public void Execute() { }
}
And a very simple abstraction to hold the list of types to NOT be disabled:
public interface IEnabledTypes
{
void Push(Type type);
Type Pull(Type type);
void Clear();
}
public class EnabledTypes : IEnabledTypes
{
public IList<Type> types = new List<Type>();
public void Push(Type type)
{
if (!this.types.Contains(type))
{
this.types.Add(type);
}
}
public Type Pull(Type type)
{
if (this.types.Contains(type))
{
return type;
}
else
{
return null;
}
}
public void Clear()
{
this.types.Clear();
}
}
Here’s the code to configure the container:
private Container ConfigureContainer()
{
Lifestyle scope = new LifetimeScopeLifestyle();
Container container = new Container();
container.EnableLifetimeScoping();
container.Register<IEnabledTypes, EnabledTypes>(scope);
container.RegisterAllOpenGeneric(
typeof(ICommandHandler<>),
typeof(CommandHandler<>),
typeof(CommandHandlerA<>),
typeof(CommandHandlerB<>));
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(DisableCommandHandlerDecorator<>),
c =>
{
// try to pull the ImplementationType from the
// collection of IEnabledTypes and say "yes" to the
// DisableCommandHandlerDecorator if the ImplementationType
// is not found
return container
.GetInstance<IEnabledTypes>()
.Pull(c.ImplementationType) == null;
});
return container;
}
And finally a test to prove it works as expected:
[Test]
public void DisableCommandHandlerDecorator_GetAll_DisablesNonEnabledTypes()
{
Container container = ConfigureContainer();
using (container.BeginLifetimeScope())
{
container
.GetInstance<IEnabledTypes>()
.Push(typeof(CommandHandlerA<A>));
var result = container
.GetAllInstances<ICommandHandler<A>>()
.ToList();
Assert.AreSame(
result[0].GetType(),
typeof(DisableCommandHandlerDecorator<A>));
Assert.AreSame(
result[1].GetType(),
typeof(CommandHandlerA<A>));
}
}
Update:
The code above had a limitation that it would make the decision to apply the decorators only the first time the type was resolved. This was proved with a second test that failed:
[Test]
public void DisableCommandHandlerDecorator_GetAllTwice_DisablesSecondNonEnabledTypes()
{
Container container = ConfigureContainer();
using (container.BeginLifetimeScope())
{
container
.GetInstance<IEnabledTypes>()
.Push(typeof(CommandHandlerA<A>));
var result = container
.GetAllInstances<ICommandHandler<A>>()
.ToList();
container
.GetInstance<IEnabledTypes>()
.Push(typeof(CommandHandler<A>));
result = container
.GetAllInstances<ICommandHandler<A>>()
.ToList();
Assert.AreSame(
result[0].GetType(),
typeof(CommandHandler<A>));
Assert.AreSame(
result[1].GetType(),
typeof(DisableCommandHandlerDecorator<A>));
}
}
I asked the question on the simpleinjector codeplex website which pointed me to an extension method that can Apply decorators at runtime
One minor change to the way the decorator is registered:
container.RegisterRuntimeDecorator(
typeof(ICommandHandler<>),
typeof(DisableCommandHandlerDecorator<>),
c =>
{
// try to pull the ImplementationType from the
// collection of IEnabledTypes and say "yes" to the
// DisableCommandHandlerDecorator if the ImplementationType
// is not found
return container
.GetInstance<IEnabledTypes>()
.Pull(c.ImplementationType) == null;
});
And violá – test 2 now works!