How to solve InvalidOperationException for constructors using HttpClientFactory in C#

A few days ago I was preparing the demo for a new article. The demo included a class with an IHttpClientFactory
service injected into the constructor. Nothing more.
Then, running the application (well, actually, executing the code), this error popped out:
System.InvalidOperationException: A suitable constructor for type ‘X’ could not be located. Ensure the type is concrete and all parameters of a public constructor are either registered as services or passed as arguments. Also ensure no extraneous arguments are provided.
How to solve it? It’s easy. But first, let me show you what I did in the wrong version.
Setting up the wrong example
For this example, I created an elementary project.
It’s a .NET 7 API project, with only one controller, GenderController
, which calls another service defined in the IGenderizeService
interface.
1 2 3 |
<span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">IGenderizeService</span> <span class="token punctuation">{</span> <span class="token return-type class-name">Task<span class="token punctuation"><</span>GenderProbability<span class="token punctuation">></span></span> <span class="token function">GetGenderProbabiliy</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">string</span></span> name<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
IGenderizeService
is implemented by a class, GenderizeService
, which is the one that fails to load and, therefore, causes the exception to be thrown. The class calls an external endpoint, parses the result, and then returns it to the caller:
1 2 3 |
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">GenderizeService</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">IGenderizeService</span></span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">readonly</span> <span class="token class-name">IHttpClientFactory</span> _httpClientFactory<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token function">GenderizeService</span><span class="token punctuation">(</span><span class="token class-name">IHttpClientFactory</span> httpClientFactory<span class="token punctuation">)</span> <span class="token punctuation">{</span> _httpClientFactory <span class="token operator">=</span> httpClientFactory<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">async</span> <span class="token return-type class-name">Task<span class="token punctuation"><</span>GenderProbability<span class="token punctuation">></span></span> <span class="token function">GetGenderProbabiliy</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">string</span></span> name<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name"><span class="token keyword">var</span></span> httpClient <span class="token operator">=</span> _httpClientFactory<span class="token punctuation">.</span><span class="token function">CreateClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name"><span class="token keyword">var</span></span> response <span class="token operator">=</span> <span class="token keyword">await</span> httpClient<span class="token punctuation">.</span><span class="token function">GetAsync</span><span class="token punctuation">(</span><span class="token interpolation-string"><span class="token string">$"?name=</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">name</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name"><span class="token keyword">var</span></span> result <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span>Content<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">ReadFromJsonAsync</span><span class="token generic class-name"><span class="token punctuation"><</span>GenderProbability<span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> result<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Finally, I’ve defined the services in the Program class, and then I’ve specified which is the base URL for the HttpClient instance generated in the GenderizeService
class:
1 |
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddScoped</span><span class="token generic class-name"><span class="token punctuation"><</span>IGenderizeService<span class="token punctuation">,</span> GenderizeService<span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddHttpClient</span><span class="token generic class-name"><span class="token punctuation"><</span>IGenderizeService<span class="token punctuation">,</span> GenderizeService<span class="token punctuation">></span></span></span><span class="token punctuation">(</span> client <span class="token operator">=></span> client<span class="token punctuation">.</span>BaseAddress <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Uri</span><span class="token punctuation">(</span><span class="token string">"https://api.genderize.io/"</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name"><span class="token keyword">var</span></span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
That’s it! Can you spot the error?
2 ways to solve the error
The error was quite simple, but it took me a while to spot:
In the constructor I was injecting an IHttpClientFactory
:
1 |
<span class="token keyword">public</span> <span class="token function">GenderizeService</span><span class="token punctuation">(</span><span class="token class-name">IHttpClientFactory</span> httpClientFactory<span class="token punctuation">)</span> |
while in the host definition I was declaring an HttpClient
for a specific class:
1 |
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span>AddHttpClient<span class="token operator"><</span>IGenderizeService<span class="token punctuation">,</span> GenderizeService<span class="token operator">></span> |
Apparently, even if we’ve specified how to create an instance for a specific class, we could not build it using an IHttpClientFactory.
So, here are 2 ways to solve it.
Use named HttpClient in HttpClientFactory
Named HttpClients are a helpful way to define a specific HttpClient and use it across different services.
It’s as simple as assigning a name to an HttpClient instance and then using the same name when you need that specific client.
So, define it in the Startup method:
1 |
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddHttpClient</span><span class="token punctuation">(</span><span class="token string">"genderize"</span><span class="token punctuation">,</span> client <span class="token operator">=></span> client<span class="token punctuation">.</span>BaseAddress <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Uri</span><span class="token punctuation">(</span><span class="token string">"https://api.genderize.io/"</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
and retrieve it using CreateClient
:
1 2 3 4 5 |
<span class="token keyword">public</span> <span class="token function">GenderizeService</span><span class="token punctuation">(</span><span class="token class-name">IHttpClientFactory</span> httpClientFactory<span class="token punctuation">)</span> <span class="token punctuation">{</span> _httpClientFactory <span class="token operator">=</span> httpClientFactory<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">async</span> <span class="token return-type class-name">Task<span class="token punctuation"><</span>GenderProbability<span class="token punctuation">></span></span> <span class="token function">GetGenderProbabiliy</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">string</span></span> name<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name"><span class="token keyword">var</span></span> httpClient <span class="token operator">=</span> _httpClientFactory<span class="token punctuation">.</span><span class="token function">CreateClient</span><span class="token punctuation">(</span><span class="token string">"genderize"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name"><span class="token keyword">var</span></span> response <span class="token operator">=</span> <span class="token keyword">await</span> httpClient<span class="token punctuation">.</span><span class="token function">GetAsync</span><span class="token punctuation">(</span><span class="token interpolation-string"><span class="token string">$"?name=</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">name</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name"><span class="token keyword">var</span></span> result <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span>Content<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">ReadFromJsonAsync</span><span class="token generic class-name"><span class="token punctuation"><</span>GenderProbability<span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> result<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
💡 Quick tip: define the HttpClient
names in a constant field shared across the whole system!
Inject HttpClient instead of IHttpClientFactory
The other way is by injecting an HttpClient
instance instead of an IHttpClientFactory
.
So we can restore the previous version of the Startup part:
1 |
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddHttpClient</span><span class="token generic class-name"><span class="token punctuation"><</span>IGenderizeService<span class="token punctuation">,</span> GenderizeService<span class="token punctuation">></span></span></span><span class="token punctuation">(</span> client <span class="token operator">=></span> client<span class="token punctuation">.</span>BaseAddress <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">Uri</span><span class="token punctuation">(</span><span class="token string">"https://api.genderize.io/"</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
and, instead of injecting an IHttpClientFactory, we can directly inject an HttpClient
instance:
1 2 3 |
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">GenderizeService</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token class-name">IGenderizeService</span></span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">readonly</span> <span class="token class-name">HttpClient</span> _httpClient<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token function">GenderizeService</span><span class="token punctuation">(</span><span class="token class-name">HttpClient</span> httpClient<span class="token punctuation">)</span> <span class="token punctuation">{</span> _httpClient <span class="token operator">=</span> httpClient<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">async</span> <span class="token return-type class-name">Task<span class="token punctuation"><</span>GenderProbability<span class="token punctuation">></span></span> <span class="token function">GetGenderProbabiliy</span><span class="token punctuation">(</span><span class="token class-name"><span class="token keyword">string</span></span> name<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name"><span class="token keyword">var</span></span> response <span class="token operator">=</span> <span class="token keyword">await</span> _httpClient<span class="token punctuation">.</span><span class="token function">GetAsync</span><span class="token punctuation">(</span><span class="token interpolation-string"><span class="token string">$"?name=</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">name</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name"><span class="token keyword">var</span></span> result <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span>Content<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">ReadFromJsonAsync</span><span class="token generic class-name"><span class="token punctuation"><</span>GenderProbability<span class="token punctuation">></span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> result<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
We no longer need to call _httpClientFactory.CreateClient
because the injected instance of HttpClient
is already customized with the settings we’ve defined at Startup.
Further readings
I’ve briefly talked about HttpClientFactory
in one article of my C# tips series:
🔗 C# Tip: use IHttpClientFactory to generate HttpClient instance | Code4IT
And, more in detail, I’ve also talked about one way to mock HttpClientFactory
instances in unit tests using Moq:
🔗 How to test HttpClientFactory with Moq | Code4IT
Finally, why do we need to use HttpClientFactories instead of HttpClients?
🔗 Use IHttpClientFactory to implement resilient HTTP requests | Microsoft Docs
Wrapping up
Yes, it was that easy!
We received the error message
A suitable constructor for type ‘X’ could not be located.
because we were mixing two ways to customize and use HttpClient
instances.
But we’ve only opened Pandora’s box: we will come back to this topic soon!
For now, Happy coding!
🐧
Source: https://www.code4it.dev/blog/solve-constructor-exception-with-httpclientfactory
