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

Jim*_*988 5 c# dependency-injection asp.net-core

在我的 .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>();
            });
}
Run Code Online (Sandbox Code Playgroud)

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

笔记:

  • Cool构造函数没有运行,所以似乎没有什么东西在那里停止。

解决方案(谢谢大家)

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

.BuildServiceProvider()是多余的。

Ste*_*ven 7

您遇到的是由注册引起的堆栈溢出异常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);
});
Run Code Online (Sandbox Code Playgroud)

如代码所示,调用会AddHttpClient<TClient>导致委托的注册。当调用该委托时,IHttpClientFactory就会解析一个问题。由此创建了IHttpClientFactory一个。HttpClient但是,就您而言,您指定了IHttpClientFactoryTClient有效地替换了原始IHttpClientFactory注册。这导致对s.GetRequiredService<IHttpClientFactory>()回调的调用自身,从而产生循环依赖和堆栈溢出。

你做错的是IHttpClientFactory作为TClient提供AddHttpClient<TClient>AddHttpClient旨在注册一个以 aHttpClient作为直接依赖项的“客户端”类。例如:

services.AddHttpClient<GitHubApiClient>() // GitHubApiClient depends on HttpClient
Run Code Online (Sandbox Code Playgroud)

然而,你并不是唯一有过错的一方。微软应该做得更好,因为:

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

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