首页 > 解决方案 > GetRequiredService 和 AddHttpClient 冻结 .NET 控制台应用程序(堆栈溢出)

问题描述

在我的 .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()是多余的。

标签: c#asp.net-coredependency-injection

解决方案


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

要了解为什么会发生这种情况,您需要查看以下代码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解析 an。从中创建IHttpClientFactory一个HttpClient。但是,在您的情况下,您指定IHttpClientFactory了您的TClient,有效地替换了原始IHttpClientFactory注册。这导致对s.GetRequiredService<IHttpClientFactory>()自身的回调调用,从而导致循环依赖和堆栈溢出。

你做错了什么是IHttpClientFactory提供TClientof AddHttpClient<TClient>AddHttpClient旨在注册一个HttpClient直接依赖的“客户端”类。例如:

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

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

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

这意味着微软堆栈还有改进的空间;-)


推荐阅读