Search code examples
c#asp.net-coredependency-injectiondotnet-httpclient

Correct usage of IHttpClientFactory for a .NET Core transient service


I'm looking into using IHttpClientFactory for calling external APIs in my ASP.NET Core app. I've seen a few examples where the client factory is created in the constructor of the service's class. Then the methods of that class call the client factory to generate an instance of HttpClient to make HTTP requests. Like the following sample code:

public class MyTransientService: IMyService
{
    private readonly IHttpClientFactory _clientFactory;

    public MyTransientService(
        IHttpClientFactory clientFactory
    )
    {
        _clientFactory = clientFactory;
    }

    public async Task<MyData> GetData()
    {
        //construct the request
        var httpClient = _clientFactory.CreateClient();
        var response = await client.SendAsync(request);
        ...
    }
}

If the service is registered as transient in Startup.cs, wouldn't a new instance of HttpClientFactory be generated each time that the service is called? A new HttpClientFactory per request? So wouldn't the following be a more efficient way to use the factory?

public class MyTransientService: IMyService
{
    private readonly HttpClient _client;

    public MyTransientService(HttpClient client)
    {
        _client = client;
    }

    public async Task<MyData> GetData()
    {
        Uri uri = new Uri(StaticUtils.AddQueryString(url, props));
        var response = await _client.SendAsync(request);
        ...
    }
}

Solution

  • I would consider creating the HttpClient yourself bad practice, on self you have control of how many is created. If MyTransientService is transient, you will end up creating a lot of socket connections (one for each instance/request) HttpClient is created to be reused.

    Take a look at Typed clients: https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpClient("hello", c =>
        {
            c.BaseAddress = new Uri("http://localhost:5000");
        })
        .AddTypedClient<MyTransientService>();
    
        services.AddControllers();
    }
    
    public class MyTransientService: IMyService
    {
        private readonly HttpClient _client;
    
        public MyTransientService(
            HtpClient client
        )
        {
            _client = client;
        }
    
        public async Task<MyData> GetData()
        {
            Uri uri = new Uri(StaticUtils.AddQueryString(url, props));
            var response = await _client.SendAsync(request);
            ...
        }
    }