You need an ability to use multiple implementations (iphone4S
, iphone5S
) of the same interface (IPhone
). There is no built-in support of this scenario by Microsoft.Extensions.DependencyInjection. However there are numerous workarounds for this limitation. One of the best in my opinion (described here) is the registering of factory and creation of required instances based on input parameter.
To use it, you should inject not an IPhone
to People
constructor, but factory Func<Type, IPhone>
. Then in Use
method where you know the type of implementation (TPhone
) you create an instance with injected factory:
public class People
{
private readonly Func<Type, IPhone> phoneFactory;
private IPhone _phone;
public People(Func<Type, IPhone> phoneFactory) => this.phoneFactory = phoneFactory;
public void Use<TPhone>(Action<IPhone> config) where TPhone : class, IPhone
{
_phone = phoneFactory(typeof(TPhone));
config(this._phone);
}
public IPhone GetPhone()
{
return this._phone;
}
}
In Main() method you should just register the factory and all possible implementations:
ServiceProvider provider = new ServiceCollection()
.AddSingleton<People>()
.AddTransient<iphone4S>()
.AddTransient<iphone5S>()
.AddTransient(factory => (Func<Type, IPhone>)(type => (IPhone)factory.GetService(type)))
.BuildServiceProvider();
provider.GetService<People>().Use<iphone5S>(phone => phone.Call("123456"));
UPDATE
If you have a long list of implementations for IPhone
interface, you could register them with reflection based approach:
var services = new ServiceCollection();
services.AddSingleton<People>()
.AddTransient(factory => (Func<Type, IPhone>)(type => (IPhone)factory.GetService(type)));
foreach (var iphoneType in Assembly.GetExecutingAssembly().GetTypes()
.Where(t => !t.IsAbstract)
.Where(t => typeof(IPhone).IsAssignableFrom(t)))
{
services.AddTransient(iphoneType);
}
var provider = services.BuildServiceProvider();
provider.GetService<People>().Use<iphone5S>(phone => phone.Call("123456"));
This will work if implementation classes are defined in the same assembly where Main()
method resides. If implementation are defined in different assemblies, you could adjust this approach by enumerating all loaded assemblies, like described in this answer.