Skip to content

Commit 7e3624e

Browse files
committed
Allow duplicate class to AddHttpClient<T>
Fixes: dotnet/extensions#2077 In 3.0 we introduced validation to try and prevent some cases of invalid usage on the factory that had lead to user bug reports. Unfortunately we blocked a few legitimate usage scenarios behind expections. In this case the common usage is for a library to register a typed client with `AddHttpClient<MyClient>(...)`. User code can then collaborate by calling the same thing, and interacting with the builder that's returned. This change explicitly allows this pattern by fine-tuning the validation. \n\nCommit migrated from dotnet/extensions@b2165d7
1 parent c4ef9be commit 7e3624e

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

src/HttpClientFactory/Http/src/DependencyInjection/HttpClientBuilderExtensions.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,12 @@ private static void ReserveClient(IHttpClientBuilder builder, Type type, string
532532
Debug.Assert(registry != null);
533533

534534
// Check for same type registered twice. This can't work because typed clients have to be unique for DI to function.
535-
if (registry.TypedClientRegistrations.TryGetValue(type, out var otherName))
535+
if (registry.TypedClientRegistrations.TryGetValue(type, out var otherName) &&
536+
537+
// Allow duplicate registrations with the same name. This is usually someone calling "AddHttpClient" once
538+
// as part of a library, and the consumer of that library doing the same thing to add further configuration.
539+
// See: https://github.com/aspnet/Extensions/issues/2077
540+
!string.Equals(name, otherName, StringComparison.Ordinal))
536541
{
537542
var message =
538543
$"The HttpClient factory already has a registered client with the type '{type.FullName}'. " +
@@ -542,7 +547,10 @@ private static void ReserveClient(IHttpClientBuilder builder, Type type, string
542547
}
543548

544549
// Check for same name registered to two types. This won't work because we rely on named options for the configuration.
545-
if (registry.NamedClientRegistrations.TryGetValue(name, out var otherType))
550+
if (registry.NamedClientRegistrations.TryGetValue(name, out var otherType) &&
551+
552+
// Allow registering the same name twice to the same type (see above).
553+
type != otherType)
546554
{
547555
var message =
548556
$"The HttpClient factory already has a registered client with the name '{name}', bound to the type '{otherType.FullName}'. " +

src/HttpClientFactory/Http/test/DependencyInjection/HttpClientFactoryServiceCollectionExtensionsTest.cs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,52 @@ public void AddHttpClient_AddTypedClient_WithDelegate_ConfiguresNamedClient()
347347
}
348348

349349
[Fact]
350-
public void AddHttpClient_AddSameTypedClientTwice_ThrowsError()
350+
public void AddHttpClient_AddSameTypedClientTwice_WithSameName_Works()
351+
{
352+
// Arrange
353+
var serviceCollection = new ServiceCollection();
354+
serviceCollection.AddHttpClient<TestTypedClient>();
355+
356+
// Act
357+
serviceCollection.AddHttpClient<TestTypedClient>(c =>
358+
{
359+
c.BaseAddress = new Uri("http://example.com");
360+
});
361+
362+
var services = serviceCollection.BuildServiceProvider();
363+
364+
// Act2
365+
var client = services.GetRequiredService<TestTypedClient>();
366+
367+
// Assert
368+
Assert.Equal("http://example.com/", client.HttpClient.BaseAddress.AbsoluteUri);
369+
}
370+
371+
[Fact]
372+
public void AddHttpClient_AddSameTypedClientTwice_WithSameName_WithAddTypedClient_Works()
373+
{
374+
// Arrange
375+
var serviceCollection = new ServiceCollection();
376+
serviceCollection.AddHttpClient<TestTypedClient>();
377+
378+
// Act
379+
serviceCollection.AddHttpClient(nameof(TestTypedClient), c =>
380+
{
381+
c.BaseAddress = new Uri("http://example.com");
382+
})
383+
.AddTypedClient<TestTypedClient>();
384+
385+
var services = serviceCollection.BuildServiceProvider();
386+
387+
// Act2
388+
var client = services.GetRequiredService<TestTypedClient>();
389+
390+
// Assert
391+
Assert.Equal("http://example.com/", client.HttpClient.BaseAddress.AbsoluteUri);
392+
}
393+
394+
[Fact]
395+
public void AddHttpClient_AddSameTypedClientTwice_WithDifferentNames_ThrowsError()
351396
{
352397
// Arrange
353398
var serviceCollection = new ServiceCollection();
@@ -365,7 +410,7 @@ public void AddHttpClient_AddSameTypedClientTwice_ThrowsError()
365410
}
366411

367412
[Fact]
368-
public void AddHttpClient_AddSameTypedClientTwice_WithAddTypedClient_ThrowsError()
413+
public void AddHttpClient_AddSameTypedClientTwice_WithDifferentNames_WithAddTypedClient_ThrowsError()
369414
{
370415
// Arrange
371416
var serviceCollection = new ServiceCollection();

0 commit comments

Comments
 (0)