Dynamically select a concrete object with a name using SimpleInjector

跟風遠走 提交于 2019-12-11 07:29:39

问题


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());

回答1:


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()));
}



回答2:


There are a few ways to go about this. https://simpleinjector.readthedocs.io/en/latest/using.html#collections https://simpleinjector.readthedocs.io/en/latest/howto.html#resolve-instances-by-key

The approach you are describing sounds like the second link. However I would generally go with the approach described in the first link and add a property Name to the IShipper interface and do something like var shipper = shippers.FirstOrDefault(x => x.Name == "FedEx");

 public interface IShipper
 {
     string Name { get; }
     void Ship(Product product)
 }

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

public void Ship(string shipperName, Product product)
{
    // TODO: Retrieve the shipper by name
    var shippers = container.GetAllInstances<IShipper>();
    var shipper = shippers.FirstOrDefault(x => x.Name == shipperName);
    // TODO: do null check on shipper
    shipper.Ship(product);
}


来源:https://stackoverflow.com/questions/42877979/dynamically-select-a-concrete-object-with-a-name-using-simpleinjector

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!