Trying to register a Http Client service using AddTypedClient throws "The HttpClient factory already has a registered client with the name" on first request.
Steps to reproduce the behavior:
services.AddHttpClient<IFoo>().AddTypedClient((hc, sp) =>
{
return new Foo();
});
public interface IFoo
{
}
public class Foo : IFoo
{
}
The api is hit and result returned
The following error occurs:
The HttpClient factory already has a registered client with the name 'IFoo', bound to the type 'WebApplication2.IFoo'. Client names are computed based on the type name without considering the namespace ('IFoo'). Use an overload of AddHttpClient that accepts a string and provide a unique name to resolve the conflict.
at Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.ReserveClient(IHttpClientBuilder builder, Type type, String name)
at Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddTypedClient[TClient](IHttpClientBuilder builder, Func``3 factory)
at WebApplication2.Startup.ConfigureServices(IServiceCollection services) in C:\Users\swanickm\source\repos\WebApplication2\WebApplication2\Startup.cs:line 29
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.InvokeCore(Object instance, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass9_0.<Invoke>g__Startup|0(IServiceCollection serviceCollection)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.Invoke(Object instance, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass8_0.<Build>b__0(IServiceCollection services)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass12_0.<UseStartup>b__0(HostBuilderContext context, IServiceCollection services)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at WebApplication2.Program.Main(String[] args) in
This works perfectly fine when using the ASP.NET Core 2.2 API template
Hi, I'm encountering the same issue with the usage of Refit. following the official documentation : https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.0#generated-clients . Any update on this issue ?
NB: this is my sample implementation :
services.AddHttpClient('myclient', client =>
{
client.BaseAddress = new Uri(endpoint);
client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json");
})
.AddTypedClient(httpClient => Refit.RestService.For<ILocalitiesService>(httpClient)) // throw here
.AddTypedClient(httpClient => Refit.RestService.For<IPoisService>(httpClient))
.AddTypedClient(httpClient => Refit.RestService.For<IPolygonsService>(httpClient))
After digging into the problem, I've found that this is a "desired regression" introduced with this PR . You can follow this issue or this Feature Request to find a potential update on it.
Until a solution is found, I will just copy-paste the function without the call to ReserveClient like this :
````csharp
public static IHttpClientBuilder AddTypedClient
where TClient : class
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}
// ReserveClient(builder, typeof(TClient), builder.Name);
builder.Services.AddTransient<TClient>(s =>
{
var httpClientFactory = s.GetRequiredService<IHttpClientFactory>();
var httpClient = httpClientFactory.CreateClient(builder.Name);
return factory(httpClient);
});
return builder;
}
```
The intent here was to help people catch mistakes in some invalid cases. It looks like this is blocking some legitimate usage, and we'll fix it.
I've just faced this issue and here's my nasty workaround using reflection.
internal static void RemoveHttpClient<T>(this IServiceCollection services)
{
var registryType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes())
.SingleOrDefault(t => t.Name == "HttpClientMappingRegistry");
var registry = services.SingleOrDefault(s => s.ServiceType == registryType)?.ImplementationInstance;
var registrations = registry?.GetType().GetProperty("TypedClientRegistrations");
var clientRegistrations = registrations?.GetValue(registry) as IDictionary<Type, string>;
clientRegistrations?.Remove(typeof(T));
}
Didn't work to me. Thus I hold any migration to 3.0 until the bug gets addressed.
In 3.0 we added validation to HttpClient Factory's registration code path to prevent some common mistakes. This new validation code blocks some valid use cases that we didn't know about.
Attempting to call AddTypedClient<T> twice on the same builder with different types throws an exception. This is a valuable shorthand way of expressing a configuration once and then binding it to multiple types.
Yes, this is a regression from 2.2
Low. This add special casing to our validation code path to allow more patterns. The impact of this is that cases that are explicitly blocked by an exception in 3.0 (but were allowed in 2.2) will be allowed again.
Most helpful comment
The intent here was to help people catch mistakes in some invalid cases. It looks like this is blocking some legitimate usage, and we'll fix it.