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>();
});
}
并最终耗尽内存并最终导致应用程序崩溃。
我错过了什么?
注:
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>
的 TClient
。 AddHttpClient
的意思是注册一个 'client' class 将 HttpClient
作为直接依赖项。例如:
services.AddHttpClient<GitHubApiClient>() // GitHubApiClient depends on HttpClient
但是,您并不是这里唯一的过错方。微软应该做得更好,因为:
- MS.DI 缺乏循环依赖检测并且过于简单导致它错过了这种类型的循环依赖。
AddHttpClient<TClient>
方法不验证允许这种情况发生的先决条件。因为用抽象 TClient
提供 AddHttpClient
是没有意义的,至少它应该阻止这种情况并抛出异常。此外,将不包含 HttpClient
的 TClient
注册为构造函数依赖项是没有意义的,因此这也应该被阻止。
这意味着 Microsoft 技术栈还有改进的空间 ;-)
在我的 .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>();
});
}
并最终耗尽内存并最终导致应用程序崩溃。 我错过了什么?
注:
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>
的 TClient
。 AddHttpClient
的意思是注册一个 'client' class 将 HttpClient
作为直接依赖项。例如:
services.AddHttpClient<GitHubApiClient>() // GitHubApiClient depends on HttpClient
但是,您并不是这里唯一的过错方。微软应该做得更好,因为:
- MS.DI 缺乏循环依赖检测并且过于简单导致它错过了这种类型的循环依赖。
AddHttpClient<TClient>
方法不验证允许这种情况发生的先决条件。因为用抽象TClient
提供AddHttpClient
是没有意义的,至少它应该阻止这种情况并抛出异常。此外,将不包含HttpClient
的TClient
注册为构造函数依赖项是没有意义的,因此这也应该被阻止。
这意味着 Microsoft 技术栈还有改进的空间 ;-)