Предположим, у вас другой провайдер SMS, и вы хотите выбрать одного из этих провайдеров на основании некоторых критериев.

Есть два способа выбрать, какая реализация интерфейса будет инстанциирована с использованием выбранного вами IOC.

Первый подход, который я не рекомендую, - это зарегистрировать свои службы и выбрать тот, который вам нужен во время выполнения.

services.AddSingleton<ISmsProvider, SmsProviderA>();
services.AddSingleton<ISmsProvider, SmsProviderB>();
services.AddSingleton<ISmsProvider, SmsProviderC>();

Затем во время выполнения вы можете разрешить правильный объект, выполнив

var services = serviceProvider.GetServices<ISmsProvider>();
var smsProviderC= services.First(o => o.GetType()== typeof(SmsProviderC));

Проблема с этим подходом в том, что когда мы вызываем serviceProvider.GetServices<ISmsProvider>();

будут созданы экземпляры всех служб, а не только той, которую вы хотите, поэтому будут созданы экземпляры SmsProviderA , SmsProviderB and SmsProviderC, и если эти службы также имеют много внутренних зависимостей, все эти зависимости также будут созданы.

Итак, рассмотрите этот подход, если ваши службы легковесны или у них нет внутренних зависимостей.

Второй подход, который я рекомендую, заключается в следующем: сначала определите свой интерфейс.

public interface ISmsProvider
{
Task<SendResult> SendSmsAsync(string to, string message);
}

Затем реализуйте свои услуги

public class SmsProviderA: ISmsProvider
{  
      private readonly HttpClient _httpClient;
       public SmsProviderA(HttpClient httpClient)
             {
                 _httpClient = httpClient;
             }
       public async Task<SmsResultDto> SendSmsAsync(SmsDto dto)
                {
                     ///do something here
                }
}
public class SmsProviderB: ISmsProvider
{  
      private readonly HttpClient _httpClient;
      public SmsProviderB(HttpClient httpClient)
           {
              _httpClient = httpClient;
           }
       public async Task<SmsResultDto> SendSmsAsync(SmsDto dto)
            {
                     ///do something here
            }
}
public class SmsProviderC: ISmsProvider
{  
     
       public async Task<SmsResultDto> SendSmsAsync(SmsDto dto)
{
                     ///do something here
                }
}

Затем зарегистрируйте услугу в выбранном вами МОК

services.AddHttpClient<SmsProviderA>();
services.AddHttpClient<SmsProviderB>();
services.AddTransient<SmsProviderC>();

А затем зарегистрируйте Func вашего Сервиса, чтобы вы могли звонить с помощью своего di

services.AddTransient<Func<string, ISms>>(serviceProvider => key =>
{
switch (key)
{
case "SmsProviderA":
return serviceProvider.GetService<SmsProviderA>();
case "SmsProviderB":
return serviceProvider.GetService<SmsProviderB>();
case "SmsProviderC":
return serviceProvider.GetService<SmsProviderC>();
default:
return serviceProvider.GetService<SmsProviderA>();
}
});

Итак, в вашем контроллере или других службах вы можете использовать свою службу и решить, какой из служб будет создан.

Помните, что сначала вы должны создать экземпляр своего преобразователя, который в данном случае является делегатом Func.

public class SendSmsController : ControllerBase
{
     private readonly Func<string, ISmsProvider> _smsServie;
     public SendSmsController(Func<string, ISms> smsServie)
        {
            _smsServie = smsServie;
        }
public async Task<ActionResult> SendSms()
           {
var serviceToBeInstantiated = "SmsProviderA";
var sendResult= await _smsServie(serviceToBeInstantiated).SendSmsAsync(number,"hi");
           }

}

Преимущество этого подхода заключается в том, что вы создаете экземпляр нужной службы только с правильным сроком действия.