Search code examples
c#dependency-injectionsimple-injector

Dynamically select a concrete object with a name using SimpleInjector


How can I use Simple Injector DI container to register multiple concrete types of IShipper interface and dynamically select them using a name?

If I can't register multiple instances of the same type, is there a different way to do with Simple Injector?

public Setup()
{
    var container = new SimpleInjector.Container();
    // TODO: Registration required for two different shippers
    container.Register<UPS, IShipper>();
}

public interface IShipper
{
    void Ship(Product product)
}

public class UPS : IShipper { ... }

public class FedEx : IShipper { ... }

public void Ship(String shipper, Product product)
{
    // TODO: Retrieve the shipper by name
    var shipper = container.GetInstance<IShipper>();
    shipper.Ship(product);
}

Ship("FedEx", new Product());

Solution

  • There are many ways of doing this, for instance you could:

    • Define a IShipperFactory that allows retrieving a IShipper.
    • Create a proxy that implements IShipper and delegates to the real IShipper implementation; this works in case the Product entity itself contains information about the shipper.
    • Create a 'dispatcherthat forwards the call to the properIShipperinternally, without exposing thisIShipper` abstraction back to the client.

    The documentation here gives some clues about how to create multiple registrations of the same abstraction.

    Here's an example using a dispatcher:

    public interface IShippingDispatcher
    {
        void Ship(string shipper, Product product);
    }
    
    public class ShippingDispatcher : IShippingDispatcher
    {
        private readonly Func<string, IShipper> factory;
        public ShippingDispatcher(Func<string, IShipper> factory) { this.factory = factory; }
        public void Ship(string shipper, Product product) => factory(shipper).Ship(product);
    }
    

    As an example, you can have the following setup with this:

    public Setup()
    {
        var container = new Container();
    
        var shippers = new Dictionary<string, InstanceProducer<IShipper>>
        {
            { "FedEx", Lifestyle.Transient.CreateProducer<IShipper, FedEx>(container) },
            { "UPS", Lifestyle.Transient.CreateProducer<IShipper, UPS>(container) },
        };
    
        container.RegisterSingleton<IShippingDispatcher>(
            new ShippingDispatcher(shipper => shippers[shipper].GetInstance()));
    }