Home > Back-end >  A suitable constructor of type does not be located
A suitable constructor of type does not be located

Time:12-11

I am trying the most simple IHttpClientFactory use case

public interface IWeatherForecast
{
    Task<string> Get();
}

class WeatherForecast : IWeatherForecast
{
    private readonly HttpClient httpClient;
    public WeatherForecast (IHttpClientFactory httpClientFactory)
    {
        httpClient = httpClientFactory.CreateClient();
    }
    public async Task<string> Get()
    {
        var resp = await httpClient.GetAsync("https://testwebapiforuseinsamples.azurewebsites.net/weatherforecast");
        return JsonConvert.SerializeObject(resp.Content.ReadAsStringAsync());
    }
}

and then instantiate it

static async Task Main(string[] args)
{
    var container = new ServiceCollection();
    container.AddHttpClient<IWeatherForecast, WeatherForecast>();
    var serviceProvider = container.BuildServiceProvider();    
    var resp = await serviceProvider.GetRequiredService<WeatherForecast>().Get();
}

However when I run it, it throws

System.InvalidOperationException: 'A suitable constructor for type 'HttpClientFactoryExample.WeatherForecast' could not be located. Ensure the type is concrete ...

Can someone point it what is wrong with this code. I was expecting that after adding WeatherForecast service to DI, I will be able to get an initialized instance of it from the container.

CodePudding user response:

When you register type in Service collection as HTTP client container.AddHttpClient<IWeatherForecast, WeatherForecast>(), it MUST contains HttpClient in constructor. In your case it should be:

public WeatherForecast(HttpClient httpClient)
{
     this.httpClient = httpClient;
}

Another option is to register separately the HttpClientFactory and your WeatherForecast service:

container.AddHttpClient();
container.AddTransient<IWeatherForecast, WeatherForecast>();

and then use the service with HttpClientFactory:

private readonly HttpClient httpClient;

public WeatherForecast(IHttpClientFactory factory)
{
    this.httpClient = factory.CreateClient();
}

CodePudding user response:

When using the type client registration that takes an abstraction like you did here

//...

container.AddHttpClient<IWeatherForecast, WeatherForecast>();

//...

it registers the abstraction as the client type, not the implementation.

Adds the IHttpClientFactory and related services to the IServiceCollection and configures a binding between the TClient type and a named HttpClient. The client name will be set to the type name of TClient.

Reference IHttpClientBuilder AddHttpClient<TClient,TImplementation>

Because you try to resolve the implementation WeatherForecast

//...

var resp = await serviceProvider.GetRequiredService<WeatherForecast>().Get();

//...

it will fail because

System.InvalidOperationException: No service for type 'WeatherForecast' has been registered.

What you should have actually resolved was the abstraction IWeatherForecast

//...

var resp = await serviceProvider.GetRequiredService<IWeatherForecast>().Get();

//...

which the container knows is mapped to the WeatherForecast implementation and will then initialize it along with its dependencies.

With that fixed, you also need to update the service to explicitly depend on HttpClient since this is a typed client registration

class WeatherForecast : IWeatherForecast {
    private readonly HttpClient httpClient;

    public WeatherForecast (HttpClient httpClient) {
        this.httpClient = httpClient;
    }

    public async Task<string> Get() {
        var resp = await httpClient.GetAsync("https://testwebapiforuseinsamples.azurewebsites.net/weatherforecast");
        return JsonConvert.SerializeObject(resp.Content.ReadAsStringAsync());
    }
}

Reference Use IHttpClientFactory to implement resilient HTTP requests

  • Related