在托管服务中注入类型化 HTTP 客户端是否会创建强制依赖?

Enr*_*one 6 c# dependency-injection ioc-container .net-core asp.net-core

我对 ASP.NET 核心依赖注入容器有疑问。这个问题专门针对ASP.NET core 3.1

基本上,我问自己,是否注入类型的HTTP客户端内部托管服务创建一个所谓的俘虏依赖于依赖注入的条款。

我试图描绘的场景如下:

public interface IStudentsApiClient  
{
  Task<IEnumerable<Student>> GetAll(CancellationToken cancellationToken);
}

public class StudentsApiClient: IStudentsApiClient 
{
  private readonly HttpClient _httpClient;
  
  public StudentsApiClient(HttpClient httpClient)
  {
    _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
  }
  
  public async Task<IEnumerable<Student>> GetAll(CancellationToken cancellationToken)
  {
      // call a GET endpoint to retrieve all the students from a third party api...
  }
}

public class StudentsPollingHostedService: BackgroundService 
{
    private readonly IStudentsApiClient _apiClient;
    
    public StudentsPollingHostedService(IStudentsApiClient apiClient) 
    {
        _apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // do something using the IStudentsApiClient service 
    }
}

// code from Startup.cs
services.AddHttpClient<IStudentsApiClient, StudentsApiClient>(); // IStudentsApiClient is registered as transient
services.AddHostedService<StudentsPollingHostedService>(); // the hosted service is registered as a singleton
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我在IStudentsApiClient单例 ( StudentsPollingHostedService) 中注入了一个瞬态依赖( ),从而创建了一个俘虏依赖。

根据我的理解,这可能会完全损害 ASP.NET 核心 HTTP 客户端工厂实现的整个目的,其目的是在消费者每次请求时返回一个新的 HTTP 客户端。在上面的代码中,我们基本上在整个应用程序生命周期内在托管服务中捕获了一个 HTTP 客户端实例。

您是否同意这是一个实际问题?

我修复上述代码的想法是更改 ,StudentsApiClient以便IHttpClientFactory用于创建HttpClient每次GetAll调用的新实例:

public interface IStudentsApiClient  
{
  Task<IEnumerable<Student>> GetAll(CancellationToken cancellationToken);
}

public class StudentsApiClient: IStudentsApiClient 
{
  private readonly IHttpClientFactory _factory;
  
  public StudentsApiClient(IHttpClientFactory factory)
  {
    _factory = factory ?? throw new ArgumentNullException(nameof(factory));
  }
  
  public async Task<IEnumerable<Student>> GetAll(CancellationToken cancellationToken)
  {
      var httpClient = _factory.CreateClient("students");
      
      // call a GET endpoint to retrieve all the students from a third party api...
  }
}

public class StudentsPollingHostedService: BackgroundService 
{
    private readonly IStudentsApiClient _apiClient;
    
    public StudentsPollingHostedService(IStudentsApiClient apiClient) 
    {
        _apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // do something using the IStudentsApiClient service 
    }
}

// code from Startup.cs
services.AddHostedService<StudentsPollingHostedService>(); // the hosted service is registered as a singleton

services.AddHttpClient("students", c => 
{
  // students HTTP client configuration goes here...
});

services.AddSingleton<IStudentsApiClient, StudentsApiClient>(); // IStudentsApiClient is now registered as a singleton 
Run Code Online (Sandbox Code Playgroud)

这是否解决了代码的第一个版本(如果有)的问题?

重要更新

对于所有对这个话题感兴趣的普通读者,在dotnet/runtime github 存储库上有基本相同的讨论。

看来这里讨论的问题是一个实际问题。

详情请看这里

我目前的决定是避免使用类型化客户端方法并注入它IHttpClientFactory,以确保完全安全。