Search code examples
c#dependency-injectionhttpclientabp-framework

How to use "ICachedServiceProvider" to inject "Typed HttpClient" correctly?


How to use "ICachedServiceProvider" to inject "Typed HttpClient" correctly?

I use Typed HttpClient and try to use ICachedServiceProvider, but I got an error.

var httpClient = _cachedServiceProvider.GetRequiredService<HttpClient>();

Error message:

System.InvalidOperationException An invalid request URI was provided. Either the request URI must be an absolute URI or BaseAddress must be set. at System.Net.Http.HttpClient.PrepareRequestMessage(HttpRequestMessage request)

But it work fine when I use Constructor Injection for Typed HttpClient

private readonly HttpClient _httpClient;

public TestService(HttpClient httpClient)
{
    _httpClient = httpClient;
}

P.S. ConfigureServices:

private void ConfigureHttpClient(ServiceConfigurationContext context)
{
    var options = context.Services.ExecutePreConfiguredActions<TestOptions>();
    context.Services.AddHttpClient<ITestService, TestService>(httpClient =>
    {
        httpClient.BaseAddress = options.BaseAddress;
    });
}

So, how to use "ICachedServiceProvider" to inject "Typed HttpClient" correctly?

Edit:

I can use IHttpClientFactory to get named HttpClient, and it work now.

var httpClientFactory = _cachedServiceProvider.GetRequiredService<IHttpClientFactory>();
var httpClient = httpClientFactory.CreateClient(nameof(ITestService));
// ...
var response = await httpClient.PostAsJsonAsync("oauth/token", testRequest);

Solution

  • Typed clients vs named clients

    First, an important clarification: in the question, TestService is the typed client.

    • Typed clients are not HttpClient instances.

      A typed client accepts an HttpClient parameter in its constructor[.]

    • Named clients are HttpClient instances.

    Next, a clarification about what the method calls do:

    Then, by definition, a typed client constructor-injects a named client.

    HttpService is practically a lightweight wrapper around longer-lived HTTP connections. There should be no need to scope-cache it.

    How to use ICachedServiceProvider to inject typed clients correctly

    Simply:

    var testService = _cachedServiceProvider.GetRequiredService<TestService>();
    

    You should constructor-inject HttpClient in TestService.

    About cached service providers and lifetimes

    If you use ICachedServiceProvider to inject IHttpClientFactory (Singleton), you will actually get Singleton IHttpClientFactory. CachedServiceProvider just caches the instance for the scope, it does not (nor should it) change the lifecycle of the registered type.