Search code examples
c#simple-injector

Implementations registered with Simple Injector's RegisterCollection are not decorated


I have a constructor (AnimalHandler) that takes two slightly different implementations of the same interface (IAnimal). Using Simple Injector, how can I automatically decorate both implementations?

The chronology of an example:

interface IAnimal {
  string Speak();
}
class Cat : IAnimal{
  public string Speak() => "Meow";
}
class Dog : IAnimal{
  public string Speak() => "Woof";
}
class AnimalSpeakLoudlyDecorator : IAnimal {
  private readonly IAnimal _decorated;
  public AnimalSpeakLoudlyDecorator(IAnimal decorated) {
    _decorated = decorated;
  }
  public string Speak() => _decorated.Speak().ToUpper();
}
class AnimalHandler {
  private readonly Cat _cat;
  private readonly Dog _dog;
  public AnimalHandler(Cat cat, Dog dog) {
    _cat = cat;
    _dog = dog;
  }
  public string HandleCat() => _cat.Speak();
  public string HandleDog() => _dog.Speak();
}

Here I realize that interfaces should be used in the constructor so that decoration could occur. I thusly create AnimalInterfaceHandler, ICat, and IDog:

interface ICat : IAnimal { }
interface IDog : IAnimal { }
class Cat : ICat {...}
class Dog : IDog {...}
class AnimalInterfaceHandler {
  private readonly ICat _cat;
  private readonly IDog _dog;
  public AnimalInterfaceHandler(ICat cat, IDog dog) {
    _cat = cat;
    _dog = dog;
  }
  public string HandleCat() => _cat.Speak();
  public string HandleDog() => _dog.Speak();
}

I register multiple interfaces with the same implementation.

var container = new Container();
var catRegistration = Lifestyle.Singleton.CreateRegistration<Cat>(container);
container.AddRegistration(typeof(ICat), catRegistration);
var dogRegistration = Lifestyle.Singleton.CreateRegistration<Dog>(container);
container.AddRegistration(typeof(IDog), dogRegistration);
container.RegisterCollection<IAnimal>(new[] { catRegistration, dogRegistration });
container.RegisterDecorator(typeof(IAnimal), typeof(AnimalSpeakLoudlyDecorator), Lifestyle.Singleton);
container.Verify();
var handler = container.GetInstance<AnimalInterfaceHandler>();
Assert.AreEqual("MEOW", handler.HandleCat());

The assert fails; the decorator is not applied despite being IAnimal being registered with RegisterCollection. Any ideas?


Solution

  • This should do the trick:

    var container = new Container();
    
    container.RegisterConditional<IAnimal, Cat>(c => c.Consumer.Target.Name == "cat");
    container.RegisterConditional<IAnimal, Dog>(c => c.Consumer.Target.Name == "dog");
    
    container.RegisterDecorator(typeof(IAnimal), typeof(AnimalSpeakLoudlyDecorator));
    
    container.Verify();
    

    This registration makes use of the RegisterConditional method, which allows to make a conditional or contextual registration based on information about the consumer. In this case it uses the name of the constructor argument it is injected into.