简体   繁体   English

服务未在servicecollection中注册

[英]Service is not registered in the servicecollection

I do not understand why after i am adding a service to IServiceCollection , when i try to get back a reference to it using ServiceProvider the value is null. 我不明白为什么在将服务添加到IServiceCollection ,当我尝试使用ServiceProvider对它的引用时,该值为null。

Startup 启动

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 HttpWrapper

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

Why is the returned service null? 为什么返回的服务为空? I first thought that its because its Transient so it would be per request..so no request at that stage -> null. 我首先以为是因为它是Transient所以它是每个请求的。所以在此阶段没有请求-> null。
But it still returns null when using AddSingleton too. 但是当也使用AddSingleton时,它仍然返回null。 Why is that service null? 为什么该服务为空?

PS What i need is : PS我需要的是:

public class TransientService{
public HttpClient sharedProperty;
}

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

I need a service that is transient but all its instances share a field ( HttpClient in my case) 我需要一个临时的服务,但其所有实例共享一个字段(以我的情况为HttpClient

PS 2 After i have changed from GetService to GetRequiredService i get the InvalidOperationException that the service was not registered.Why would that happen ? PS 2GetService更改为GetRequiredService我收到未注册该服务的InvalidOperationException为什么会这样? I have also changed from the to the non lambda insertion: 我也从改为非lambda插入:
services.AddTransient() still with the same problem. services.AddTransient()仍然存在相同的问题。

I can't reproduce the problem. 我无法重现该问题。

GetService will return null if the requested service hasn't been registered. 如果尚未注册请求的服务,则GetService将返回null。 If GetRequiredService was called, it would throw an exception. 如果GetRequiredService ,它将引发异常。

There are two problems in this code. 这段代码有两个问题。

First, this line : 首先,这行:

services.AddTransient(y =>wrapper);

registers a factory function that returns the same HttpWrapper instance, not the HttpWrapper instance itself. 注册一个工厂函数 ,该函数返回相同的HttpWrapper实例,而不是HttpWrapper实例本身。 That function will return whatever wrapper contains. 该函数将返回wrapper包含的内容。

Second, AddTransient says this is a transient service and yet, the same instance will be returned every time. 其次, AddTransient说这是一个临时服务,但是每次都会返回相同的实例。

If the same service is used every time, the registration should use AddSingleton : 如果每次都使用相同的服务,则注册应使用AddSingleton

services.AddSingleton<HttpWrapper>();

If a new instance is needed each time, it should be : 如果每次都需要一个实例,则应为:

services.AddTransient<HttpWrapper>();

Test Code 测试代码

I used this code to test the problem and can't reproduce it : 我用此代码测试了问题,无法重现:

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

The class will be created without any issues. 该类将被创建,没有任何问题。 The only way for this to fail is for wrapper to be null. 失败的唯一方法是wrapper为null。 I use GetRequiredService to force an exception if no registration is found. 如果找不到注册,我使用GetRequiredService强制执行异常。

Typed Http Clients 类型的Http客户端

From the comments it appears that the real issue is how to create a service that uses HttpClient properly. 从评论看来, 真正的问题是如何创建正确使用HttpClient的服务。 Reusing the same instance will improve performance as it keeps existing TCP connections in a connection pool. 重用同一实例将提高性能,因为它将现有的TCP连接保留在连接池中。 It still needs to be recycled periodically though to handle changing DNS addresses. 尽管它仍然需要定期回收以处理不断变化的DNS地址。 Using a transient HttpClient is bad but so is using a single HttpClient instance. 使用瞬态HttpClient不好,但是使用单个HttpClient实例也是如此。

The HttpClientFactory takes care of both concerns by pooling and recycling the HttpClientHandler instances that actually perform the HTTP requests for each HttpClient. HttpClientFactory通过池化并回收实际对每个HttpClient执行HTTP请求的HttpClientHandler实例来解决这两个问题。 The article Use HttpClientFactory to implement resilient HTTP requests explains how this works but long story short, simply adding : 使用HttpClientFactory实施弹性HTTP请求的文章解释了它是如何工作的,但总的来说,只需添加:

services.AddHttpClient<ICatalogService, CatalogService>();

Will ensure that every instance of CatalogService will get a properly handled HttpClient . 将确保CatalogService每个实例都将得到正确处理的HttpClient All CatalogService needs to do is accept an HttpClient in its constructor and use it in its methods : 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);
        ...
    }
}

The AddHttpClient extension method is available in the Microsoft.Extensions.Http package Microsoft.Extensions.Http包中提供了AddHttpClient扩展方法。

Adapting this to the question, HttpWrapper should accept an HttpClient parameter in its constructor : 对此问题进行适应, HttpWrapper应该在其构造函数中接受HttpClient参数:

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

    public string TestUrl = "aaa";

}

And get registered with AddHttpClient : 并注册AddHttpClient

services.AddHttpClient<HttpWrapper>();

After that, it can be resolved with : 之后,可以通过以下方式解决:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM