问题
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 aIShipper
. - Create a proxy that implements
IShipper
and delegates to the realIShipper
implementation; this works in case theProduct
entity itself contains information about the shipper. - Create a 'dispatcher
that forwards the call to the proper
IShipperinternally, without exposing this
IShipper` 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