Reputation: 75
I'm trying to follow best practices to create loosely coupled code but I still need to have a deeper understanding of Dependency Injection.
So I was wondering if this code respect this concept since I'm still instantiating a class in the properties. I'm talking about the HttpClient
here.
Can anyone tell me if I'm making a good usage of Dependency Injection? Or does programming against a concrete instance of HttpClient
create a tight coupling?
Program.cs:
builder.Services.AddHttpClient("AuthService", client =>
{
client.BaseAddress = new Uri(builder.Configuration.GetValue<string>("Address")!);
}).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (_, _, _, _) => true,
AllowAutoRedirect = false,
UseCookies = true
});
Auth.cs
public class Auth(IHttpClientFactory httpClientFactory)
{
...
private HttpClient client = httpClientFactory.CreateClient("Auth");
...
public async Task<Result<Uri>> GetStuff()
{
try
{
using var response = await client.GetAsync("www.example.com");
if (response.StatusCode == HttpStatusCode.OK)
return Result<Uri>.Success(cachedLocation);
}
catch (HttpRequestException ex)
{
return Result<Uri>.Failure($"HTTP request failed: {ex.Message}");
}
}
}
Upvotes: 3
Views: 114
Reputation: 7434
I think when answering this question it is important to differentiate between general principals of Dependency Injection and some very specific peculiarities of the HttpClient
class.
Very broadly speaking, you should prefer to inject interfaces, abstract classes or factories. This promotes loosely coupled easily testable code.
The HttpClient
is a bit of a special case. It does not have an IHttpClient
interface to inject, and it is not abstract. When it was first introduced a lot of example code just newed up a client and used it. In large applications this caused a problem with socket limitation as mentioned in the comments and there is a good write up here
So shortly afterward Microsoft introduced/promoted the HttpClientFactory
class which was intended to be used in singleton scope in your application. It had an interface and could be injected and it was a big improvement. Since then there have been a variety of extension methods that now support injecting a variety of different clients into your classes. MS Docs show a number of ways
This built-in functionality promotes fairly loosely coupled code, which leaves testability. You can google "C# Mock Http Client" and find a lot of different resources - this SO question has some good answers
Upvotes: 3
Reputation: 233100
In our book Dependency Injection Principles, Practices, and Patterns, Steven van Deursen and I describe Dependency Injection (DI) as a set of patterns and principles that enable loose coupling, testability, and other beneficial designs. Constructor Injection, as used in the OP's Auth
class is such a pattern, so I'd consider this a case of DI.
Another important component of DI is the Composition Root, which is often the program's entry point; here, Program.cs
. You need to compose all the object graphs somewhere, and the entry point is usually the best place to do it.
What the OP showcases is what I call Pure DI. Not only do I consider it a valid DI technique, I actually consider it better than using a DI Container, because Pure DI is type-safe, whereas containers aren't.
So I'd do something similar to what is shown in the OP, although these days I'm not so keen on injecting factories. That, however, is another discussion for another day.
Upvotes: 3