Home > Mobile >  .Net Core HttpClient - Typed Client lifetime
.Net Core HttpClient - Typed Client lifetime

Time:07-20

I am trying to inject IHttpClientFactory in my service for external api calls and use with DI Container. Because creating a new HttpClient object for per request has critic problems as you can see in this article

https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests?mvpaWT.mc_id=AZ-MVP-5003875

for using a "Typed Client", i need to register my services like this in startup.cs

services.AddHttpClient<IMyService, MyService>(options => 
{
   options.BaseUrl = new Uri("https://myApi.com");
   // you can also give default request headers here.
   // And you can use polly for resillience of your services.
});

And my service

public class MyService : IMyService
{
  private HttpClient _client;
  private ILogger<MyService> _logger;
  private IAnotherSingletonService _anotherSingletonService;
   
  public MyService(
    HttpClient client,
    ILogger<MyService> logger,
    IAnotherSingletonService anotherSingletonService)
  {
    _client = client;
    _logger = logger;
    _anotherSingletonService = anotherSingletonService;
  }

  public async Task MyMethod()
  {
    var URL = "/api/products"; //base url is in startup.cs already
    var response = await _client.GetAsync(URL);

    //use other registered services...
  }
}

So, my question is AddHttpClient adds my service with Transient Lifetime. This is called "Typed Client". But i want to make MyService singleton. And i want to know how can i do this by using a Typed Client.

EDIT - Secod Way:

Here i tried a basic usage of IHttpClientFactory

in Startup.cs

services.AddHttpClient();
services.AddSingleton<IMyService, MyService>();

MyService

public class MyService : IMyService
{
  private IHttpClientFactory _httpClientFactory;
  private ILogger<MyService> _logger;
  private IAnotherSingletonService _anotherSingletonService;
   
  public MyService(
    IHttpClientFactory httpClientFactory,
    ILogger<MyService> logger,
    IAnotherSingletonService anotherSingletonService)
  {
    _httpClientFactory= httpClientFactory;
    _logger = logger;
    _anotherSingletonService = anotherSingletonService;
  }

  public async Task MyMethod()
  {
    using(var httpClient = _httpClientFactory.CreateClient())
    {
        var URL = "/api/products"; //base url is in startup.cs already
        var response = await httpClient.GetAsync(URL);
    }


    //use other registered services...
  }
}

CodePudding user response:

There are two problems with the original HttpClient:

  • Using using (new HttpClient()) can cause port exhaustion, because it's using a separate connection per HttpClient instance.
  • Using static HttpClient can miss DNS updates, because it's using a single connection indefinitely.

TL;DR: Since .NET Core 2.1, you can use a static HttpClient. You still shouldn't use using (new HttpClient()).

Longer answer:

The problem isn't actually about HttpClient instances; it's about the socket lifetime, which is controlled by the inner HttpMessageHandler (or the last HttpMessageHandler if you have delegating handlers).

In .NET Core 2.0 and earlier, this was HttpClientHandler by default. HttpClientHandler makes one socket connection per instance. So, to work around this problem, the ASP.NET team created IHttpClientFactory, which pools the HttpClientHandlers (not the HttpClient wrappers, which are created and destroyed as needed).

In .NET Core 2.1 and newer, the default inner handler is SocketsHttpHandler, which has its own pool of connections. So, on these platforms, a single static HttpClient instance is fine. You may wish to set the PooledConnectionLifetime property if you want to proactively detect DNS changes.

Some people still prefer IHttpClientFactory because of its fluent DI syntax, but it's no longer required.

And in your case, since your service is a singleton, you can't use the typed clients (which is the nicest DI syntax IMO). So you have two options:

  1. Use IHttpClientFactory and get an HttpClient as necessary (as in your edit). In this case, the factory will pool HttpClientHandlers for the HttpClient instances (and each HttpClientHandler is a connection).
  2. Use a static HttpClient. In this case, the SocketsHttpHandler created by the HttpClient will pool its own connections.

There are some advantages to IHttpClientFactory (e.g., you could use a named client and still get the nice DI syntax); and there are some advantages to SocketsHttpHandler (e.g., performance and cross-platform consistency). In the end, the decision is up to you; there isn't one clear winner.

  • Related