Carlos Torrecillas
Carlos Torrecillas

Reputation: 5736

SimpleInjector dictionary as constructor argument

I'm trying to set up a container to register a class that takes in its constructor a IDictionary<string, IInterface> so in my IoC I would like to know how to get a named instance (if that feature is available in SI). An example of what I want to achieve is:

container.Register<IOtherInterface>(() => 
    new OtherInterface(new Dictionary<string, IInterface>
    {
        { "a", container.GetInstance<IInterface>("named-a") },
        { "b", container.GetInstance<IInterface>("named-b") },
    });

Is there any way to configure this?

Updated:

The implementation of the interface is the same object with different parameters and that's an example I haven't been able to see in the SI documentation https://simpleinjector.readthedocs.io/en/latest/howto.html#resolve-instances-by-key . I'd like to avoid the fact that I have to instantiate a new Interface(..with the relevant parameters...) for each value in the dictionary that's why I thought that naming a registration was something available in SI.

Maybe my approach is wrong and I should try to establish the dependencies in a different way.

Example of usage:

public class OtherInterface : IOtherInterface
{
   private readonly IDictionary<string, IInterface> _interfces;

   public OtherInterface(IDictionary<string, IInterface> interfaces)
   {
       _interfaces = interfaces;
   }

   public void DoSomething(MyRequest request)
   {
       if(_interfaces.ContainKey(request.SelectMyInterface))
       {
           _interfaces[request.SelectMyInterface].DoSpecificStuff();
       }
   }
}

I could potentially extend the interface IInterface and apply the strategy pattern here having a method like Applies(string type) but I have used the dictionary approach in the past with Ninject.

Upvotes: 0

Views: 1028

Answers (1)

Steven
Steven

Reputation: 172646

Keyed registrations are done in Simple Injector by creating InstanceProducer instances 'manually' as follows:

var a = Lifestyle.Transient.CreateProducer<IInterface, A>(container);
var b = Lifestyle.Transient.CreateProducer<IInterface, B>(container);

Using these instance producers, you can make the dictionary registration as follows:

container.Register<Dictionary<string, IInterface>(() => new Dictionary<string, IInterface>
{
    { "a", a.GetInstance() },
    { "b", b.GetInstance() },
});

Your OtherInterface looks like a dispatcher to me. If it's only job is to route an incoming request, I would say that's okay.

But instead of injecting a dictionary with a complete list of created instances, I would like to propose a bit different design:

public class OtherInterface : IOtherInterface
{
   private readonly IDictionary<string, Func<IInterface>> _interfces;

   public OtherInterface(IDictionary<string, Func<IInterface>> interfaces) {
       _interfaces = interfaces;
   }

   public void DoSomething(MyRequest request) =>
       _interfaces[request.SelectMyInterface].Invoke().DoSpecificStuff();
}

Here the dictionary contains a Func<IInterface>. This way an implementation can be created on the fly, without having to create all implementations on every call.

You can register this as follows:

container.RegisterSingleton<IOtherInterface>(new OtherInterface(
    new Dictionary<string, Func<IInterface>>
    {
        { "a", CreateProducer<A>() },
        { "b", CreateProducer<B>() },
    });

private Func<IInterface> CreateProducer<T>() where T : IInterface =>
    Lifestyle.Transient.CreateProducer<IInterface, T>(this.container).GetInstance;

Alternatively, you might benefit from a design as described here (and explained in more detail here), although it's a bit hard to tell with the limited amount of context given by your question.

Upvotes: 1

Related Questions