diff --git a/src/HttpClientFactory/Polly/src/DependencyInjection/PollyServiceCollectionExtensions.cs b/src/HttpClientFactory/Polly/src/DependencyInjection/PollyServiceCollectionExtensions.cs index 0146d472ad1..5af4490dd9c 100644 --- a/src/HttpClientFactory/Polly/src/DependencyInjection/PollyServiceCollectionExtensions.cs +++ b/src/HttpClientFactory/Polly/src/DependencyInjection/PollyServiceCollectionExtensions.cs @@ -8,8 +8,8 @@ namespace Microsoft.Extensions.DependencyInjection { /// - /// Provides convenience extension methods to register and - /// in the service collection. + /// Provides convenience extension methods to register and + /// in the service collection. /// public static class PollyServiceCollectionExtensions { @@ -19,7 +19,7 @@ public static class PollyServiceCollectionExtensions /// the newly created registry. /// /// The . - /// The newly created . + /// The newly created . public static IPolicyRegistry AddPolicyRegistry(this IServiceCollection services) { if (services == null) @@ -61,5 +61,42 @@ public static IPolicyRegistry AddPolicyRegistry(this IServiceCollection return registry; } + + /// + /// Registers an empty in the service collection with service types + /// , and and + /// uses the specified delegate to configure it. + /// + /// The . + /// A delegate that is used to configure an . + /// The provided . + public static IServiceCollection AddPolicyRegistry(this IServiceCollection services, Action> configureRegistry) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configureRegistry == null) + { + throw new ArgumentNullException(nameof(configureRegistry)); + } + + // Create an empty registry, configure it and register it as an instance. + // This is the best way to get a single instance registered using both interfaces. + services.AddSingleton(serviceProvider => + { + var registry = new PolicyRegistry(); + + configureRegistry(serviceProvider, registry); + + return registry; + }); + + services.AddSingleton>(serviceProvider => serviceProvider.GetRequiredService()); + services.AddSingleton>(serviceProvider => serviceProvider.GetRequiredService()); + + return services; + } } } diff --git a/src/HttpClientFactory/Polly/test/DependencyInjection/PollyHttpClientBuilderExtensionsTest.cs b/src/HttpClientFactory/Polly/test/DependencyInjection/PollyHttpClientBuilderExtensionsTest.cs index d8e6b3af3ee..3ec05f1ecfc 100644 --- a/src/HttpClientFactory/Polly/test/DependencyInjection/PollyHttpClientBuilderExtensionsTest.cs +++ b/src/HttpClientFactory/Polly/test/DependencyInjection/PollyHttpClientBuilderExtensionsTest.cs @@ -255,7 +255,7 @@ public async Task AddPolicyHandlerFromRegistry_Dynamic_AddsPolicyHandler() public async Task AddTransientHttpErrorPolicy_AddsPolicyHandler_HandlesStatusCode(HttpStatusCode statusCode) { // Arrange - var handler = new SequenceMessageHandler() + using var handler = new SequenceMessageHandler() { Responses = { @@ -303,7 +303,7 @@ public async Task AddTransientHttpErrorPolicy_AddsPolicyHandler_HandlesStatusCod public async Task AddTransientHttpErrorPolicy_AddsPolicyHandler_HandlesHttpRequestException() { // Arrange - var handler = new SequenceMessageHandler() + using var handler = new SequenceMessageHandler() { Responses = { @@ -415,6 +415,59 @@ public async Task AddPolicyHandlerFromRegistry_PolicySelectorWithKey_AddsPolicyH Assert.True(registry.ContainsKey("host2")); } + [Fact] + public async Task AddPolicyHandlerFromRegistry_WithConfigureDelegate_AddsPolicyHandler() + { + var options = new PollyPolicyOptions() + { + PolicyName = "retrypolicy" + }; + + var serviceCollection = new ServiceCollection(); + + serviceCollection.AddSingleton(options); + + serviceCollection.AddPolicyRegistry((serviceProvider, registry) => + { + string policyName = serviceProvider.GetRequiredService().PolicyName; + + registry.Add>(policyName, RetryPolicy); + }); + + HttpMessageHandlerBuilder builder = null; + + // Act1 + serviceCollection.AddHttpClient("example.com", c => c.BaseAddress = new Uri("http://example.com")) + .AddPolicyHandlerFromRegistry(options.PolicyName) + .ConfigureHttpMessageHandlerBuilder(b => + { + b.PrimaryHandler = PrimaryHandler; + + builder = b; + }); + + var services = serviceCollection.BuildServiceProvider(); + var factory = services.GetRequiredService(); + + // Act2 + var client = factory.CreateClient("example.com"); + + // Assert + Assert.NotNull(client); + + Assert.Collection( + builder.AdditionalHandlers, + h => Assert.IsType(h), + h => Assert.IsType(h), + h => Assert.IsType(h)); + + // Act 3 + var response = await client.SendAsync(new HttpRequestMessage()); + + // Assert + Assert.Equal(HttpStatusCode.Created, response.StatusCode); + } + // Throws an exception or fails on even numbered requests, otherwise succeeds. private class FaultyMessageHandler : DelegatingHandler { @@ -447,5 +500,10 @@ protected override Task SendAsync(HttpRequestMessage reques return Task.FromResult(func(request)); } } + + private class PollyPolicyOptions + { + public string PolicyName { get; set; } + } } }