服务未在servicecollection中注册

时间:2019-01-18 09:35:08

标签: c# asp.net-core dependency-injection service-provider

我不明白为什么要向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是按请求的。.so在那个阶段没有请求-> null。
但是当使用AddSingleton时,它仍然返回null。 为什么该服务为空?

PS 我需要的是:

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该服务未注册。为什么会这样?我还从插入更改为非lambda插入:
services.AddTransient()仍然存在相同的问题。

1 个答案:

答案 0 :(得分:3)

我无法重现问题。

如果尚未注册请求的服务,

GetService将返回null。如果调用GetRequiredService,它将引发异常。

此代码中有两个问题。

首先,这一行:

services.AddTransient(y =>wrapper);

注册一个工厂函数,该函数返回相同的HttpWrapper实例,而不是HttpWrapper实例本身。该函数将返回wrapper包含的内容。

第二,AddTransient说这是一个临时服务,但是每次都会返回相同的实例。

如果每次都使用相同的服务,则注册应使用AddSingleton

services.AddSingleton<HttpWrapper>();

如果每次都需要一个 new 实例,则该实例应为:

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);
    }
}

该类将被创建而没有任何问题。失败的唯一方法是使wrapper为null。如果找不到注册,我使用GetRequiredService强制例外。

键入的Http客户端

从注释中可以看出, real 问题是如何创建正确使用HttpClient的服务。重用同一实例将提高性能,因为它将现有的TCP连接保留在连接池中。尽管它仍然需要定期回收以处理不断变化的DNS地址。使用临时HttpClient不好,但是使用单个HttpClient实例也是如此。

HttpClientFactory通过池化并回收实际上为每个HttpClient执行HTTP请求的 HttpClientHandler 实例来解决这两个问题。文章Use HttpClientFactory to implement resilient HTTP requests解释了这是如何工作的,但总之,总之,只需添加:

services.AddHttpClient<ICatalogService, CatalogService>();

将确保CatalogService的每个实例都将得到正确处理的HttpClientCatalogService要做的就是在其构造函数中接受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);
        ...
    }
}

Microsoft.Extensions.Http软件包中提供了AddHttpClient扩展方法

对此进行调整,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>();