GetRequiredService 和 AddHttpClient 冻结 .NET 控制台应用程序(堆栈溢出)

GetRequiredService and AddHttpClient freeze .NET console application (stack overflow)

在我的 .NET Core 控制台应用程序中,这一行冻结:

services.GetRequiredService<ISomething>();

在以下代码中:

interface ISomething { }
class Cool : ISomething
{
    public Cool(IHttpClientFactory factory) { }
}

class Program
{
    static async Task Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();
        await host.StartAsync();

        // Freezes here and maxes out memory
        ISomething internalApiConnector = host.Services.GetRequiredService<ISomething>();

        await host.RunAsync();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((_, services) =>
            {
                services.AddHttpClient<IHttpClientFactory>();
                services.AddSingleton<ISomething, Cool>();
            });
}

并最终耗尽内存并最终导致应用程序崩溃。 我错过了什么?

注:

解决方案(谢谢大家):

serviceCollection.AddHttpClient<IHttpClientFactory>();需要改成 serviceCollection.AddHttpClient();

.BuildServiceProvider() 是多余的。

您遇到的是由 AddHttpClient<IHttpClientFactory> 注册引起的堆栈溢出异常。这会导致 MS.DI 未检测到的循环依赖性,导致不幸的堆栈溢出。

要了解为什么会发生这种情况,您需要查看 code for HttpClientBuilderExtensions:

builder.Services.AddTransient<TClient>(s =>
{
    var httpClientFactory = s.GetRequiredService<IHttpClientFactory>();
    var httpClient = httpClientFactory.CreateClient(builder.Name);

    var typedClientFactory = s.GetRequiredService<ITypedHttpClientFactory<TClient>>();
    return typedClientFactory.CreateClient(httpClient);
});

如代码所示,对 AddHttpClient<TClient> 的调用导致委托的注册。调用该委托时,将解析 IHttpClientFactory。从那 IHttpClientFactory 创建一个 HttpClient。但是,在您的情况下,您为 TClient 指定了 IHttpClientFactory,有效地替换了原来的 IHttpClientFactory 注册。这导致对 s.GetRequiredService<IHttpClientFactory>() 的调用回调到自身,因此循环依赖和堆栈溢出。

你做错的是提供 IHttpClientFactory 作为 AddHttpClient<TClient>TClientAddHttpClient 的意思是注册一个 'client' class 将 HttpClient 作为直接依赖项。例如:

services.AddHttpClient<GitHubApiClient>() // GitHubApiClient depends on HttpClient

但是,您并不是这里唯一的过错方。微软应该做得更好,因为:

  • MS.DI 缺乏循环依赖检测并且过于简单导致它错过了这种类型的循环依赖。
  • AddHttpClient<TClient> 方法不验证允许这种情况发生的先决条件。因为用抽象 TClient 提供 AddHttpClient 是没有意义的,至少它应该阻止这种情况并抛出异常。此外,将不包含 HttpClientTClient 注册为构造函数依赖项是没有意义的,因此这也应该被阻止。

这意味着 Microsoft 技术栈还有改进的空间 ;-)