Skip to content

HCR005

Do not separately register a typed client already registered by AddHttpClient<T>().

Why

AddHttpClient<TClient>() already registers the typed client. Adding a separate AddTransient<TClient>(), AddScoped<TClient>(), or AddSingleton<TClient>() can override or conflict with the intended typed-client registration.

Bad

services.AddHttpClient<PaymentsClient>();
services.AddTransient<PaymentsClient>();

Better

services.AddHttpClient<PaymentsClient>();

Current Detection

The implementation builds a lightweight IServiceCollection registration model across the compilation and reports duplicate standalone AddSingleton, AddScoped, or AddTransient registrations whose service or implementation type matches a typed client registered with AddHttpClient.

This catches traditional Startup methods, minimal hosting configuration, extension methods, registrations split across files, visible qualified type names, one- or two-generic-argument registration forms, typeof(...) service registrations such as AddTransient(typeof(PaymentsClient)) including parenthesized or null-forgiving type expressions, and visible factory registrations such as AddTransient<IPaymentsClient>(sp => new PaymentsClient()). Registration receivers, including aliased IServiceCollection parameters, are validated semantically when their declarations are available, and resolved registration methods are limited to Microsoft DI extensions. Visible minimal-hosting builders whose Services member is typed as IServiceCollection are supported; unresolved builder lookalikes are skipped instead of borrowing unrelated Services members in the same file. Resolved registration type names are compared namespace-sensitively, so same-named typed clients in different namespaces are skipped even when one registration uses an unqualified type name.

Code Fix

The code fix removes a bare duplicate standalone service-registration statement and leaves the AddHttpClient<TClient>() registration intact. It is intentionally withheld when the duplicate has a factory/policy argument or participates in a fluent chain, because removing the whole statement could discard construction or configuration policy; those cases require manual review.

Suppression

Prefer removing the duplicate registration. Suppress only when a custom extension method mimics these names but does not register services.

References