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
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 perHttpClient
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())
.
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 HttpClientHandler
s (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:
- Use
IHttpClientFactory
and get anHttpClient
as necessary (as in your edit). In this case, the factory will poolHttpClientHandler
s for theHttpClient
instances (and eachHttpClientHandler
is a connection). - Use a
static HttpClient
. In this case, theSocketsHttpHandler
created by theHttpClient
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.