Сервис не зарегистрирован в сервисе коллекции - PullRequest
0 голосов
/ 18 января 2019

Я не понимаю, почему после добавления услуги к IServiceCollection, когда я пытаюсь получить ссылку на нее, используя ServiceProvider, значение равно нулю.

Запуск

public void ConfigureServices(IServiceCollection services)
{
    Startup.ConfigureServicesForDebugging?.Invoke(services);

    try
    {

        services.AddTransient<HttpWrapper>();

        ServiceProvider prov = services.BuildServiceProvider();
        HttpWrapper returned=prov.GetRequiredService<HttpWrapper>();

        if (returned == null)
        {
            Console.WriteLine("is null");
        }
        else Console.WriteLine(returned.Test);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }
}

HttpWrapper

public class HttpWrapper
{
    public HttpClient cl = new HttpClient();
    public string Test = "aaa";
}

Почему возвращаемая служба пуста? Сначала я подумал, что это потому, что он Transient, поэтому он будет обрабатываться по запросу .. поэтому на этом этапе нет запросов -> ноль.
Но он все равно возвращает ноль при использовании AddSingleton. Почему этот сервис не работает?

P.S Что мне нужно:

public class TransientService{
public HttpClient sharedProperty;
}

public void ConfigureServices(IServiceCollection services)
{
  HttpClient client=new HttpClient();
  services.AddTransient( new TransientService{ sharedProperty=client});
}

Мне нужен временный сервис, но все его экземпляры имеют общее поле (HttpClient в моем случае)

PS 2 После того, как я изменил с GetService на GetRequiredService, я получаю InvalidOperationException, что служба не была зарегистрирована. Почему это произойдет? Я также изменил с не лямбда-вставки:
services.AddTransient () по-прежнему с той же проблемой.

1 Ответ

0 голосов
/ 18 января 2019

Я не могу воспроизвести проблему.

GetService вернет ноль, если запрошенная услуга не была зарегистрирована. Если бы GetRequiredService был вызван, это вызвало бы исключение.

В этом коде есть две проблемы.

Сначала эта строка:

services.AddTransient(y =>wrapper);

регистрирует фабричную функцию , которая возвращает тот же экземпляр HttpWrapper, а не сам экземпляр HttpWrapper. Эта функция вернет все, что wrapper содержит.

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

Если один и тот же сервис используется каждый раз, при регистрации следует использовать AddSingleton:

services.AddSingleton<HttpWrapper>();

Если каждый раз требуется новый экземпляр, он должен быть:

services.AddTransient<HttpWrapper>();

Тестовый код

Я использовал этот код для проверки проблемы и не могу воспроизвести ее:

static void Main(string[] args)
{
    IServiceCollection services = new ServiceCollection();
    try
    {
        HttpWrapper wrapper = new HttpWrapper();
        services.AddTransient(provider => wrapper);

        ServiceProvider prov = services.BuildServiceProvider();
        HttpWrapper returned = prov.GetRequiredService<HttpWrapper>();

        Console.WriteLine("No problem");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }
}

Класс будет создан без проблем. Единственный способ этого не сделать - 1036 * быть нулевым. Я использую GetRequiredService, чтобы вызвать исключение, если регистрация не найдена.

Типизированные Http-клиенты

Из комментариев видно, что проблема real заключается в том, как создать службу, которая правильно использует HttpClient. Повторное использование одного и того же экземпляра улучшит производительность, поскольку оно будет поддерживать существующие TCP-соединения в пуле соединений. Это все еще необходимо периодически перерабатывать, хотя для обработки изменения адресов DNS. Использование временного HttpClient - это плохо, но так же используется один экземпляр HttpClient.

HttpClientFactory решает обе проблемы, объединяя и перерабатывая экземпляры HttpClientHandler , которые фактически выполняют HTTP-запросы для каждого HttpClient. Статья Использование HttpClientFactory для реализации устойчивых HTTP-запросов объясняет, как это работает, но вкратце, просто добавив:

services.AddHttpClient<ICatalogService, CatalogService>();

Обеспечит правильную обработку каждого экземпляра CatalogService HttpClient. Все, что нужно CatalogService, это принять HttpClient в его конструкторе и использовать его в своих методах:

public class CatalogService : ICatalogService
{
    private readonly HttpClient _httpClient;

    public CatalogService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<Catalog> GetCatalogItems(int page, int take, 
                                           int? brand, int? type)
    {
        ....
        var responseString = await _httpClient.GetStringAsync(uri);
        ...
    }
}

Метод расширения AddHttpClient доступен в пакете Microsoft.Extensions.Http

Адаптируя это к вопросу, HttpWrapper должен принять параметр HttpClient в своем конструкторе:

public class HttpWrapper 
{
    private readonly HttpClient _cl;
    public HttpWrapper(HttpClient client)
    {
        _cl=client;
    }

    public string TestUrl = "aaa";

}

И зарегистрируйтесь с AddHttpClient:

services.AddHttpClient<HttpWrapper>();

После этого его можно решить с помощью:

var returned = prov.GetRequiredService<HttpWrapper>();
...