A tiny companion for Scrutor that lets its
assembly-scanning register each matched class as a typed
HttpClient
(via IHttpClientFactory) instead of a plain service — the one thing Scrutor
deliberately doesn't do out of the box.
services.Scan(scan => scan
.FromAssemblyOf<IWeatherClient>()
.AddClasses(c => c.AssignableTo<IApiClient>())
.AsMatchingInterface()
.AsHttpClient("weather")); // ← every scanned client becomes a typed HttpClientdotnet add package Scrutor.Extensions.HttpClient- Register a named
HttpClientwith your shared configuration. - Make your API clients implement a common marker interface (and, for
.AsMatchingInterface(), anI{ClassName}interface each). - Scan for them and finish the chain with
.AsHttpClient("<name>").
// 1. A named client carrying the shared config (base address, handlers, Polly, …).
services.AddHttpClient("weather", client =>
{
client.BaseAddress = new Uri("https://api.example.com");
});
// 2 + 3. Register every IApiClient implementation as a typed client of "weather".
services.Scan(scan => scan
.FromAssemblyOf<IWeatherClient>()
.AddClasses(classes => classes.AssignableTo<IApiClient>())
.AsMatchingInterface()
.AsHttpClient("weather"));Now each scanned client is resolved from IHttpClientFactory with the "weather"
client's configuration injected:
public sealed class WeatherClient(HttpClient http) : IWeatherClient
{
public Task<Forecast?> GetAsync(string city) =>
http.GetFromJsonAsync<Forecast>($"forecast/{city}");
}| Call | Behaviour |
|---|---|
.AsHttpClient("name") |
Registers each scanned class as a typed client bound to the named client "name" — they share its configuration. |
.AsHttpClient() |
Registers each scanned class as a typed client with its own default client (named after the service type). |
Both are IServiceTypeSelector extensions, so they slot onto the end of any Scrutor
.Scan(...) chain (.AsMatchingInterface(), .AsSelf(), .As<T>(), …).
- Scrutor 7.x — the package major tracks Scrutor's major (
7.x↔ Scrutor 7.x). - Targets
net8.0andnetstandard2.0(so it's usable from .NET Framework 4.6.2+, .NET Core 2.0+, and modern .NET alike).
This bridges Scrutor's runtime assembly scan and closes the generic
AddHttpClient<TClient,TImplementation> over the scanned types via reflection, so it
is not trim- or Native-AOT-safe. The public methods are annotated with
[RequiresUnreferencedCode] / [RequiresDynamicCode], so a trimmed/AOT build gets an
honest warning at the call site rather than silent breakage.
CI is defined in C# with Fallout (a NUKE fork); the GitHub Actions workflow is generated from the build, never hand-edited.
./build.cmd Test # run the spec suite
./build.cmd Pack # test + pack the NuGet package