0

I want provide a services to do GetPhone()

like this

provider.GetService<People>().GetPhone(iphone4S).Use<IPhone>(phone => phone.Call("123456"));

and I try in People() to do GetPhone() but it's no way

how to do this services?

my Services Code

public interface IPhone
{
    string Call(string Num);
}
public class People 
{
    private IPhone _phone;
    public People(IPhone phone) => this._phone = phone;

    public void Use<TPhone>(Action<IPhone> config)
        where TPhone : class, IPhone
    {
        config(this._phone);
    }
    public IPhone GetPhone()
    {
        return this._phone;
    }
}
public class iphone4S : IPhone
{
    ....
}

public class iphone5S : IPhone
{
    ....
}

Main program:

ServiceProvider provider = new ServiceCollection()
         .AddSingleton<People>()
         .BuildServiceProvider();
Provider.GetService<People>().GetPhone(iphone4S).Use<IPhone>(phone => phone.Call("123456"));
CodeFuller
  • 30,317
  • 3
  • 63
  • 79
MingHong Zheng
  • 175
  • 4
  • 13
  • What are you trying to do? If you want to use different implementations for a service based on the current model, just detect the model during runtime and register only the appropriate service. If you want to find an object based on some user-provided data though, you are asking for data retrieval, not dependency injection. – Panagiotis Kanavos Mar 12 '18 at 11:44

1 Answers1

1

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.

CodeFuller
  • 30,317
  • 3
  • 63
  • 79