From 88d8612788791b3882d8a2eb9cea11b052a37feb Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Mon, 5 Jun 2023 10:19:28 +0200 Subject: [PATCH] Introduce OutcomeArguments and use them in delegates --- .../Utils/Helper.CircuitBreaker.cs | 4 +- .../Utils/Helper.Hedging.cs | 2 +- .../Utils/Helper.MultipleStrategies.cs | 2 +- .../Utils/Helper.Retry.cs | 4 +- .../AdvancedCircuitBreakerOptionsTests.cs | 4 +- .../CircuitBreakerPredicateArgumentsTests.cs | 6 +- ...itBreakerResilienceStrategyBuilderTests.cs | 20 +++---- .../CircuitBreakerResilienceStrategyTests.cs | 12 ++-- .../Controller/CircuitStateControllerTests.cs | 26 ++++----- .../OnCircuitClosedArgumentsTests.cs | 6 +- .../OnCircuitHalfOpenedArgumentsTests.cs | 6 +- .../OnCircuitOpenedArgumentsTests.cs | 5 +- .../SimpleCircuitBreakerOptionsTests.cs | 4 +- .../Fallback/FallbackHandlerTests.cs | 37 ++++++------ ...esilienceStrategyBuilderExtensionsTests.cs | 10 ++-- .../FallbackResilienceStrategyTests.cs | 38 ++++++------- .../Controller/HedgingControllerTests.cs | 2 +- .../HedgingExecutionContextTests.cs | 2 +- .../Hedging/Controller/TaskExecutionTests.cs | 2 +- .../Hedging/HandleHedgingArgumentsTests.cs | 4 +- .../Hedging/HedgingHandlerTests.cs | 31 +++++----- ...esilienceStrategyBuilderExtensionsTests.cs | 6 +- .../Hedging/HedgingResilienceStrategyTests.cs | 38 ++++++------- ...uesTests.CircuitBreakerStateSharing_959.cs | 2 +- .../Issues/IssuesTests.FlowingContext_849.cs | 2 +- .../IssuesTests.HandleMultipleResults_898.cs | 4 +- .../Retry/RetryDelayArgumentsTests.cs | 3 +- ...esilienceStrategyBuilderExtensionsTests.cs | 11 ++-- .../Retry/RetryResilienceStrategyTests.cs | 54 +++++++++--------- .../Retry/ShouldRetryArgumentsTests.cs | 4 +- .../Strategy/EventInvokerTests.cs | 29 +++++----- .../Strategy/GeneratorInvokerTests.cs | 37 ++++++------ .../Strategy/OnRetryArgumentsTests.cs | 3 +- .../Strategy/OutcomeArgumentsTests.cs | 35 ++++++++++++ .../Strategy/PredicateBuilderTests.cs | 2 +- .../Strategy/PredicateInvokerTests.cs | 29 +++++----- .../ResilienceStrategyTelemetryTests.cs | 16 +++--- .../CircuitBreakerPredicateArguments.cs | 5 +- .../CircuitBreakerResilienceStrategy.cs | 4 +- .../CircuitBreakerStrategyOptions.cs | 6 +- .../Controller/CircuitStateController.cs | 16 +++--- .../OnCircuitClosedArguments.cs | 5 +- .../OnCircuitHalfOpenedArguments.cs | 5 +- .../OnCircuitOpenedArguments.cs | 5 +- .../Fallback/FallbackHandler.Handler.cs | 6 +- .../Fallback/FallbackHandler.TResult.cs | 11 ++-- src/Polly.Core/Fallback/FallbackHandler.cs | 6 +- .../Fallback/FallbackResilienceStrategy.cs | 12 ++-- ...backResilienceStrategyBuilderExtensions.cs | 2 +- .../FallbackStrategyOptions.TResult.cs | 6 +- .../Fallback/FallbackStrategyOptions.cs | 2 +- .../Fallback/HandleFallbackArguments.cs | 5 +- .../Fallback/OnFallbackArguments.cs | 5 +- .../Fallback/VoidFallbackHandler.cs | 4 +- .../Hedging/Controller/TaskExecution.cs | 3 +- .../Hedging/HandleHedgingArguments.cs | 5 +- ...HedgingActionGeneratorArguments.TResult.cs | 4 +- .../HedgingActionGeneratorArguments.cs | 4 +- .../Hedging/HedgingDelayArguments.cs | 4 +- .../Hedging/HedgingHandler.Handler.cs | 6 +- .../Hedging/HedgingHandler.TResult.cs | 2 +- .../Hedging/HedgingResilienceStrategy.cs | 6 +- .../Hedging/HedgingStrategyOptions.TResult.cs | 4 +- .../Hedging/HedgingStrategyOptions.cs | 2 +- src/Polly.Core/Hedging/OnHedgingArguments.cs | 5 +- src/Polly.Core/Hedging/VoidHedgingHandler.cs | 2 +- src/Polly.Core/README.md | 26 ++++----- src/Polly.Core/Retry/OnRetryArguments.cs | 5 +- src/Polly.Core/Retry/RetryDelayArguments.cs | 5 +- .../Retry/RetryResilienceStrategy.cs | 16 +++--- ...etryResilienceStrategyBuilderExtensions.cs | 2 +- .../Retry/RetryStrategyOptions.TResult.cs | 6 +- src/Polly.Core/Retry/ShouldRetryArguments.cs | 5 +- src/Polly.Core/Strategy/EventInvoker.cs | 19 +++---- src/Polly.Core/Strategy/GeneratorInvoker.cs | 19 +++---- .../Strategy/IResilienceArguments.cs | 12 ---- src/Polly.Core/Strategy/OutcomeArguments.cs | 56 +++++++++++++++++++ src/Polly.Core/Strategy/PredicateBuilder.cs | 12 ++-- src/Polly.Core/Strategy/PredicateInvoker.cs | 19 +++---- .../ResilienceStrategyBuilderContext.cs | 9 +-- .../Strategy/ResilienceStrategyTelemetry.cs | 15 +++-- .../Telemetry/TelemetryEventArguments.cs | 8 ++- src/Polly.Core/Timeout/OnTimeoutArguments.cs | 4 +- .../Timeout/TimeoutGeneratorArguments.cs | 4 +- .../Timeout/TimeoutResilienceStrategy.cs | 2 +- ...s.OnCircuitBreakWithServiceProvider_796.cs | 4 +- ...esilienceTelemetryDiagnosticSourceTests.cs | 15 ++++- .../Telemetry/EnrichmentContext.Pool.cs | 8 +-- .../Telemetry/EnrichmentContext.cs | 4 +- .../Telemetry/EnrichmentUtil.cs | 2 +- .../RateLimiterResilienceStrategyTests.cs | 2 +- .../OnRateLimiterRejectedArguments.cs | 3 +- .../RateLimiterResilienceStrategy.cs | 2 +- src/Polly.TestUtils/TestArguments.cs | 7 +-- src/Polly.TestUtils/TestUtilities.cs | 18 ++++-- 95 files changed, 503 insertions(+), 460 deletions(-) create mode 100644 src/Polly.Core.Tests/Strategy/OutcomeArgumentsTests.cs delete mode 100644 src/Polly.Core/Strategy/IResilienceArguments.cs create mode 100644 src/Polly.Core/Strategy/OutcomeArguments.cs diff --git a/src/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs b/src/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs index 053a614e78b..9363aab490d 100644 --- a/src/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs +++ b/src/Polly.Core.Benchmarks/Utils/Helper.CircuitBreaker.cs @@ -11,7 +11,7 @@ public static object CreateOpenedCircuitBreaker(PollyVersion version, bool handl var manualControl = new CircuitBreakerManualControl(); var options = new AdvancedCircuitBreakerStrategyOptions { - ShouldHandle = (_, _) => PredicateResult.True, + ShouldHandle = _ => PredicateResult.True, ManualControl = manualControl, }; @@ -56,7 +56,7 @@ public static object CreateCircuitBreaker(PollyVersion technology) SamplingDuration = TimeSpan.FromSeconds(30), MinimumThroughput = 10, BreakDuration = TimeSpan.FromSeconds(5), - ShouldHandle = (outcome, _) => outcome switch + ShouldHandle = args => args switch { { Exception: InvalidOperationException } => PredicateResult.True, { Result: string result } when result == Failure => PredicateResult.True, diff --git a/src/Polly.Core.Benchmarks/Utils/Helper.Hedging.cs b/src/Polly.Core.Benchmarks/Utils/Helper.Hedging.cs index 71d8a5dc314..db4b1560288 100644 --- a/src/Polly.Core.Benchmarks/Utils/Helper.Hedging.cs +++ b/src/Polly.Core.Benchmarks/Utils/Helper.Hedging.cs @@ -13,7 +13,7 @@ public static ResilienceStrategy CreateHedging() { builder.AddHedging(new HedgingStrategyOptions { - ShouldHandle = (outcome, _) => new ValueTask(outcome.Result == Failure), + ShouldHandle = args => new ValueTask(args.Result == Failure), HedgingActionGenerator = args => () => Task.FromResult("hedged response"), }); }); diff --git a/src/Polly.Core.Benchmarks/Utils/Helper.MultipleStrategies.cs b/src/Polly.Core.Benchmarks/Utils/Helper.MultipleStrategies.cs index ebd518cb677..8674b575266 100644 --- a/src/Polly.Core.Benchmarks/Utils/Helper.MultipleStrategies.cs +++ b/src/Polly.Core.Benchmarks/Utils/Helper.MultipleStrategies.cs @@ -34,7 +34,7 @@ internal static partial class Helper SamplingDuration = TimeSpan.FromSeconds(30), MinimumThroughput = 10, BreakDuration = TimeSpan.FromSeconds(5), - ShouldHandle = (outcome, _) => outcome switch + ShouldHandle = args => args switch { { Exception: InvalidOperationException } => PredicateResult.True, { Result: string result } when result == Failure => PredicateResult.True, diff --git a/src/Polly.Core.Benchmarks/Utils/Helper.Retry.cs b/src/Polly.Core.Benchmarks/Utils/Helper.Retry.cs index 787c19e1029..940a7a37908 100644 --- a/src/Polly.Core.Benchmarks/Utils/Helper.Retry.cs +++ b/src/Polly.Core.Benchmarks/Utils/Helper.Retry.cs @@ -24,13 +24,13 @@ public static object CreateRetry(PollyVersion technology) RetryCount = 3, BackoffType = RetryBackoffType.Constant, BaseDelay = delay, - ShouldRetry = (outcome, _) => outcome switch + ShouldRetry = args => args switch { { Exception: InvalidOperationException } => PredicateResult.True, { Result: string result } when result == Failure => PredicateResult.True, _ => PredicateResult.False }, - OnRetry = (_, _) => default + OnRetry = _ => default }); }), _ => throw new NotSupportedException() diff --git a/src/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs b/src/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs index 4f18d96c01e..ddebc857337 100644 --- a/src/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs +++ b/src/Polly.Core.Tests/CircuitBreaker/AdvancedCircuitBreakerOptionsTests.cs @@ -29,7 +29,7 @@ public void Ctor_Defaults() options.MinimumThroughput = 2; options.SamplingDuration = TimeSpan.FromMilliseconds(500); - options.ShouldHandle = (_, _) => PredicateResult.True; + options.ShouldHandle = _ => PredicateResult.True; ValidationHelper.ValidateObject(options, "Dummy."); } @@ -55,7 +55,7 @@ public void Ctor_Generic_Defaults() options.MinimumThroughput = 2; options.SamplingDuration = TimeSpan.FromMilliseconds(500); - options.ShouldHandle = (_, _) => PredicateResult.True; + options.ShouldHandle = _ => PredicateResult.True; ValidationHelper.ValidateObject(options, "Dummy."); } diff --git a/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerPredicateArgumentsTests.cs b/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerPredicateArgumentsTests.cs index 7baf0ba4d6c..780f9d1fcfe 100644 --- a/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerPredicateArgumentsTests.cs +++ b/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerPredicateArgumentsTests.cs @@ -7,10 +7,6 @@ public class CircuitBreakerPredicateArgumentsTests [Fact] public void Ctor_Ok() { - var context = ResilienceContext.Get(); - - var args = new CircuitBreakerPredicateArguments(context); - - args.Context.Should().Be(context); + this.Invoking(_ => new CircuitBreakerPredicateArguments()).Should().NotThrow(); } } diff --git a/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderTests.cs b/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderTests.cs index ba7b5ad2be6..5913a903020 100644 --- a/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderTests.cs +++ b/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderTests.cs @@ -11,11 +11,11 @@ public class CircuitBreakerResilienceStrategyBuilderTests { builder => builder.AddAdvancedCircuitBreaker(new AdvancedCircuitBreakerStrategyOptions { - ShouldHandle = (_, _) => PredicateResult.True + ShouldHandle = _ => PredicateResult.True }), builder => builder.AddSimpleCircuitBreaker(new SimpleCircuitBreakerStrategyOptions { - ShouldHandle = (_, _) => PredicateResult.True + ShouldHandle = _ => PredicateResult.True }), }; @@ -23,11 +23,11 @@ public class CircuitBreakerResilienceStrategyBuilderTests { builder => builder.AddAdvancedCircuitBreaker(new AdvancedCircuitBreakerStrategyOptions { - ShouldHandle = (_, _) => PredicateResult.True + ShouldHandle = _ => PredicateResult.True }), builder => builder.AddSimpleCircuitBreaker(new SimpleCircuitBreakerStrategyOptions { - ShouldHandle = (_, _) => PredicateResult.True + ShouldHandle = _ => PredicateResult.True }), }; @@ -100,9 +100,9 @@ public void AddCircuitBreaker_IntegrationTest() { FailureThreshold = 5, BreakDuration = TimeSpan.FromMilliseconds(500), - ShouldHandle = (outcome, _) => new ValueTask(outcome.Result is -1), - OnOpened = (_, _) => { opened++; return default; }, - OnClosed = (_, _) => { closed++; return default; }, + ShouldHandle = args => new ValueTask(args.Result is -1), + OnOpened = _ => { opened++; return default; }, + OnClosed = _ => { closed++; return default; }, OnHalfOpened = (_) => { halfOpened++; return default; } }; @@ -151,9 +151,9 @@ public void AddAdvancedCircuitBreaker_IntegrationTest() MinimumThroughput = 10, SamplingDuration = TimeSpan.FromSeconds(10), BreakDuration = TimeSpan.FromSeconds(1), - ShouldHandle = (outcome, _) => new ValueTask(outcome.Result is -1), - OnOpened = (_, _) => { opened++; return default; }, - OnClosed = (_, _) => { closed++; return default; }, + ShouldHandle = args => new ValueTask(args.Result is -1), + OnOpened = _ => { opened++; return default; }, + OnClosed = _ => { closed++; return default; }, OnHalfOpened = (_) => { halfOpened++; return default; } }; diff --git a/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs b/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs index 2aacb3ed013..fd69272f012 100644 --- a/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs +++ b/src/Polly.Core.Tests/CircuitBreaker/CircuitBreakerResilienceStrategyTests.cs @@ -50,7 +50,7 @@ public void Ctor_StateProvider_EnsureAttached() [Fact] public async Task Ctor_ManualControl_EnsureAttached() { - _options.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException); + _options.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException); _options.ManualControl = new CircuitBreakerManualControl(); var strategy = Create(); @@ -74,7 +74,7 @@ public async Task Ctor_ManualControl_EnsureAttached() [Fact] public void Execute_HandledResult_OnFailureCalled() { - _options.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result is -1); + _options.ShouldHandle = args => new ValueTask(args.Result is -1); var strategy = Create(); var shouldBreak = false; @@ -87,7 +87,7 @@ public void Execute_HandledResult_OnFailureCalled() [Fact] public void Execute_UnhandledResult_OnActionSuccess() { - _options.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result is -1); + _options.ShouldHandle = args => new ValueTask(args.Result is -1); var strategy = Create(); _behavior.Setup(v => v.OnActionSuccess(CircuitState.Closed)); @@ -99,7 +99,7 @@ public void Execute_UnhandledResult_OnActionSuccess() [Fact] public void Execute_HandledException_OnFailureCalled() { - _options.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException); + _options.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException); var strategy = Create(); var shouldBreak = false; @@ -113,7 +113,7 @@ public void Execute_HandledException_OnFailureCalled() [Fact] public void Execute_UnhandledException_NoCalls() { - _options.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException); + _options.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException); var strategy = Create(); strategy.Invoking(s => s.Execute(_ => throw new ArgumentException())).Should().Throw(); @@ -126,7 +126,7 @@ public void Execute_UnhandledException_NoCalls() [Fact] public void Execute_Ok() { - _options.ShouldHandle = (_, _) => PredicateResult.False; + _options.ShouldHandle = _ => PredicateResult.False; _behavior.Setup(v => v.OnActionSuccess(CircuitState.Closed)); Create().Invoking(s => s.Execute(_ => { })).Should().NotThrow(); diff --git a/src/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs b/src/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs index 2ead5c3ff8e..1130afb8080 100644 --- a/src/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs +++ b/src/Polly.Core.Tests/CircuitBreaker/Controller/CircuitStateControllerTests.cs @@ -10,7 +10,7 @@ public class CircuitStateControllerTests private readonly FakeTimeProvider _timeProvider = new(); private readonly CircuitBreakerStrategyOptions _options = new SimpleCircuitBreakerStrategyOptions(); private readonly Mock _circuitBehavior = new(MockBehavior.Strict); - private readonly Action _onTelemetry = _ => { }; + private readonly Action _onTelemetry = _ => { }; private DateTimeOffset _utcNow = DateTimeOffset.UtcNow; public CircuitStateControllerTests() => _timeProvider.Setup(v => v.UtcNow).Returns(() => _utcNow); @@ -30,13 +30,13 @@ public async Task IsolateAsync_Ok() { // arrange bool called = false; - _options.OnOpened = (outcome, args) => + _options.OnOpened = args => { - args.BreakDuration.Should().Be(TimeSpan.MaxValue); + args.Arguments.BreakDuration.Should().Be(TimeSpan.MaxValue); args.Context.IsSynchronous.Should().BeFalse(); args.Context.IsVoid.Should().BeTrue(); - args.IsManual.Should().BeTrue(); - outcome.IsVoidResult.Should().BeTrue(); + args.Arguments.IsManual.Should().BeTrue(); + args.Outcome.IsVoidResult.Should().BeTrue(); called = true; return default; }; @@ -67,12 +67,12 @@ public async Task BreakAsync_Ok() { // arrange bool called = false; - _options.OnClosed = (outcome, args) => + _options.OnClosed = args => { args.Context.IsSynchronous.Should().BeFalse(); args.Context.IsVoid.Should().BeTrue(); - args.IsManual.Should().BeTrue(); - outcome.IsVoidResult.Should().BeTrue(); + args.Arguments.IsManual.Should().BeTrue(); + args.Outcome.IsVoidResult.Should().BeTrue(); called = true; return default; }; @@ -232,9 +232,9 @@ public async Task OnActionSuccess_EnsureCorrectBehavior(CircuitState state, Circ { // arrange var called = false; - _options.OnClosed = (_, args) => + _options.OnClosed = args => { - args.IsManual.Should().BeFalse(); + args.Arguments.IsManual.Should().BeFalse(); called = true; return default; }; @@ -272,15 +272,15 @@ public async Task OnActionFailureAsync_EnsureCorrectBehavior(CircuitState state, { // arrange var called = false; - _options.OnOpened = (_, args) => + _options.OnOpened = args => { if (state == CircuitState.Isolated) { - args.IsManual.Should().BeTrue(); + args.Arguments.IsManual.Should().BeTrue(); } else { - args.IsManual.Should().BeFalse(); + args.Arguments.IsManual.Should().BeFalse(); } called = true; diff --git a/src/Polly.Core.Tests/CircuitBreaker/OnCircuitClosedArgumentsTests.cs b/src/Polly.Core.Tests/CircuitBreaker/OnCircuitClosedArgumentsTests.cs index 5b323ae97d4..2af1480cbdd 100644 --- a/src/Polly.Core.Tests/CircuitBreaker/OnCircuitClosedArgumentsTests.cs +++ b/src/Polly.Core.Tests/CircuitBreaker/OnCircuitClosedArgumentsTests.cs @@ -7,11 +7,7 @@ public class OnCircuitClosedArgumentsTests [Fact] public void Ctor_Ok() { - var context = ResilienceContext.Get(); - - var args = new OnCircuitClosedArguments(context, true); - - args.Context.Should().Be(context); + var args = new OnCircuitClosedArguments(true); args.IsManual.Should().BeTrue(); } } diff --git a/src/Polly.Core.Tests/CircuitBreaker/OnCircuitHalfOpenedArgumentsTests.cs b/src/Polly.Core.Tests/CircuitBreaker/OnCircuitHalfOpenedArgumentsTests.cs index f515f8e317d..49046096470 100644 --- a/src/Polly.Core.Tests/CircuitBreaker/OnCircuitHalfOpenedArgumentsTests.cs +++ b/src/Polly.Core.Tests/CircuitBreaker/OnCircuitHalfOpenedArgumentsTests.cs @@ -7,10 +7,6 @@ public class OnCircuitHalfOpenedArgumentsTests [Fact] public void Ctor_Ok() { - var context = ResilienceContext.Get(); - - var args = new OnCircuitHalfOpenedArguments(context); - - args.Context.Should().Be(context); + this.Invoking(_ => new OnCircuitHalfOpenedArguments()).Should().NotThrow(); } } diff --git a/src/Polly.Core.Tests/CircuitBreaker/OnCircuitOpenedArgumentsTests.cs b/src/Polly.Core.Tests/CircuitBreaker/OnCircuitOpenedArgumentsTests.cs index 02c1bfc2faa..7ed5fd3159d 100644 --- a/src/Polly.Core.Tests/CircuitBreaker/OnCircuitOpenedArgumentsTests.cs +++ b/src/Polly.Core.Tests/CircuitBreaker/OnCircuitOpenedArgumentsTests.cs @@ -7,11 +7,8 @@ public class OnCircuitOpenedArgumentsTests [Fact] public void Ctor_Ok() { - var context = ResilienceContext.Get(); + var args = new OnCircuitOpenedArguments(TimeSpan.FromSeconds(2), true); - var args = new OnCircuitOpenedArguments(context, TimeSpan.FromSeconds(2), true); - - args.Context.Should().Be(context); args.BreakDuration.Should().Be(TimeSpan.FromSeconds(2)); args.IsManual.Should().BeTrue(); } diff --git a/src/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs b/src/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs index a75c6bd07e3..ac6927de037 100644 --- a/src/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs +++ b/src/Polly.Core.Tests/CircuitBreaker/SimpleCircuitBreakerOptionsTests.cs @@ -26,7 +26,7 @@ public void Ctor_Defaults() options.FailureThreshold = 1; options.BreakDuration = TimeSpan.FromMilliseconds(500); - options.ShouldHandle = (_, _) => PredicateResult.True; + options.ShouldHandle = _ => PredicateResult.True; ValidationHelper.ValidateObject(options, "Dummy."); } @@ -48,7 +48,7 @@ public void Ctor_Generic_Defaults() options.FailureThreshold = 1; options.BreakDuration = TimeSpan.FromMilliseconds(500); - options.ShouldHandle = (_, _) => PredicateResult.True; + options.ShouldHandle = _ => PredicateResult.True; ValidationHelper.ValidateObject(options, "Dummy."); } diff --git a/src/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs b/src/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs index 4c733c63db5..4b3a4592bab 100644 --- a/src/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs +++ b/src/Polly.Core.Tests/Fallback/FallbackHandlerTests.cs @@ -64,17 +64,17 @@ public async Task SetFallback_Ok() var handler = new FallbackHandler() .SetFallback(handler => { - handler.FallbackAction = (_, _) => new ValueTask(0); - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result == -1); + handler.FallbackAction = _ => new ValueTask(0); + handler.ShouldHandle = args => new ValueTask(args.Result == -1); }) .CreateHandler(); - var args = new HandleFallbackArguments(ResilienceContext.Get()); + var args = new HandleFallbackArguments(); handler.Should().NotBeNull(); - var action = await handler!.ShouldHandleAsync(new Outcome(-1), args); - (await action!(new Outcome(-1), args)).Should().Be(0); + var action = await handler!.ShouldHandleAsync(new(ResilienceContext.Get(), new Outcome(-1), args)); + (await action!(new(ResilienceContext.Get(), new Outcome(-1), args))).Should().Be(0); - action = await handler!.ShouldHandleAsync(new Outcome(0), args); + action = await handler!.ShouldHandleAsync(new(ResilienceContext.Get(), new Outcome(0), args)); action.Should().BeNull(); } @@ -84,18 +84,18 @@ public async Task SetVoidFallback_Ok() var handler = new FallbackHandler() .SetVoidFallback(handler => { - handler.FallbackAction = (_, _) => default; - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException); + handler.FallbackAction = _ => default; + handler.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException); }) .CreateHandler(); - var args = new HandleFallbackArguments(ResilienceContext.Get()); + var args = new HandleFallbackArguments(); handler.Should().NotBeNull(); - var action = await handler!.ShouldHandleAsync(new Outcome(new InvalidOperationException()), args); + var action = await handler!.ShouldHandleAsync(new(ResilienceContext.Get(), new Outcome(new InvalidOperationException()), args)); action.Should().NotBeNull(); - (await action!(new Outcome(new InvalidOperationException()), args)).Should().Be(VoidResult.Instance); + (await action!(new(ResilienceContext.Get(), new Outcome(new InvalidOperationException()), args))).Should().Be(VoidResult.Instance); - action = await handler!.ShouldHandleAsync(new Outcome(new ArgumentNullException()), args); + action = await handler!.ShouldHandleAsync(new(ResilienceContext.Get(), new Outcome(new ArgumentNullException()), args)); action.Should().BeNull(); } @@ -105,18 +105,19 @@ public async Task ShouldHandleAsync_UnknownResultType_Null() var handler = new FallbackHandler() .SetFallback(handler => { - handler.FallbackAction = (_, _) => default; - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException); + handler.FallbackAction = _ => default; + handler.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException); }) .SetFallback(handler => { - handler.FallbackAction = (_, _) => default; - handler.ShouldHandle = (_, _) => PredicateResult.True; + handler.FallbackAction = _ => default; + handler.ShouldHandle = _ => PredicateResult.True; }) .CreateHandler(); - var args = new HandleFallbackArguments(ResilienceContext.Get()); - var action = await handler!.ShouldHandleAsync(new Outcome(new InvalidOperationException()), args); + var context = ResilienceContext.Get(); + var args = new HandleFallbackArguments(); + var action = await handler!.ShouldHandleAsync(new(context, new Outcome(new InvalidOperationException()), args)); action.Should().BeNull(); } } diff --git a/src/Polly.Core.Tests/Fallback/FallbackResilienceStrategyBuilderExtensionsTests.cs b/src/Polly.Core.Tests/Fallback/FallbackResilienceStrategyBuilderExtensionsTests.cs index 6191c2b2aa6..9be89ab7ce2 100644 --- a/src/Polly.Core.Tests/Fallback/FallbackResilienceStrategyBuilderExtensionsTests.cs +++ b/src/Polly.Core.Tests/Fallback/FallbackResilienceStrategyBuilderExtensionsTests.cs @@ -14,13 +14,13 @@ public class FallbackResilienceStrategyBuilderExtensionsTests { builder.AddFallback(new FallbackStrategyOptions { - FallbackAction = (_, _) => new ValueTask(0), - ShouldHandle = (_, _) => PredicateResult.False, + FallbackAction = _ => new ValueTask(0), + ShouldHandle = _ => PredicateResult.False, }); }, builder => { - builder.AddFallback(handle => handle.HandleResult(1), (_, _) => new ValueTask(0)); + builder.AddFallback(handle => handle.HandleResult(1), _ => new ValueTask(0)); }, }; @@ -39,8 +39,8 @@ public void AddFallback_Ok() var options = new FallbackStrategyOptions(); options.Handler.SetFallback(handler => { - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException || outcome.Result == -1); - handler.FallbackAction = (_, args) => + handler.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException || args.Result == -1); + handler.FallbackAction = args => { args.Context.Should().NotBeNull(); return new ValueTask(1); diff --git a/src/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs b/src/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs index 32dc9dea4ba..0852e9f87a8 100644 --- a/src/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs +++ b/src/Polly.Core.Tests/Fallback/FallbackResilienceStrategyTests.cs @@ -6,7 +6,7 @@ namespace Polly.Core.Tests.Fallback; public class FallbackResilienceStrategyTests { private readonly FallbackStrategyOptions _options = new(); - private readonly List _args = new(); + private readonly List _args = new(); private readonly ResilienceStrategyTelemetry _telemetry; public FallbackResilienceStrategyTests() => _telemetry = TestUtilities.CreateResilienceTelemetry(args => _args.Add(args)); @@ -29,13 +29,13 @@ public void NoHandler_Skips() public void Handle_Result_Ok() { var called = false; - _options.OnFallback = (_, _) => { called = true; return default; }; + _options.OnFallback = _ => { called = true; return default; }; _options.Handler.SetFallback(handler => { - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result == -1); - handler.FallbackAction = (outcome, args) => + handler.ShouldHandle = args => new ValueTask(args.Result == -1); + handler.FallbackAction = args => { - outcome.Result.Should().Be(-1); + args.Result.Should().Be(-1); args.Context.Should().NotBeNull(); return new ValueTask(0); }; @@ -43,7 +43,7 @@ public void Handle_Result_Ok() Create().Execute(_ => -1).Should().Be(0); - _args.Should().ContainSingle(v => v is HandleFallbackArguments); + _args.Should().ContainSingle(v => v is OnFallbackArguments); called.Should().BeTrue(); } @@ -52,8 +52,8 @@ public void Handle_Result_FallbackActionThrows() { _options.Handler.SetFallback(handler => { - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result == -1); - handler.FallbackAction = (_, _) => throw new InvalidOperationException(); + handler.ShouldHandle = args => new ValueTask(args.Result == -1); + handler.FallbackAction = _ => throw new InvalidOperationException(); }); Create().Invoking(s => s.Execute(_ => -1)).Should().Throw(); @@ -63,13 +63,13 @@ public void Handle_Result_FallbackActionThrows() public void Handle_Exception_Ok() { var called = false; - _options.OnFallback = (_, _) => { called = true; return default; }; + _options.OnFallback = _ => { called = true; return default; }; _options.Handler.SetFallback(handler => { - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException); - handler.FallbackAction = (outcome, args) => + handler.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException); + handler.FallbackAction = args => { - outcome.Exception.Should().BeOfType(); + args.Exception.Should().BeOfType(); args.Context.Should().NotBeNull(); return new ValueTask(0); }; @@ -77,7 +77,7 @@ public void Handle_Exception_Ok() Create().Execute(_ => throw new InvalidOperationException()).Should().Be(0); - _args.Should().ContainSingle(v => v is HandleFallbackArguments); + _args.Should().ContainSingle(v => v is OnFallbackArguments); called.Should().BeTrue(); } @@ -87,11 +87,11 @@ public void Handle_UnhandledException_Ok() var called = false; var fallbackActionCalled = false; - _options.OnFallback = (_, _) => { called = true; return default; }; + _options.OnFallback = _ => { called = true; return default; }; _options.Handler.SetFallback(handler => { - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException); - handler.FallbackAction = (_, _) => + handler.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException); + handler.FallbackAction = _ => { fallbackActionCalled = true; return new ValueTask(0); @@ -111,11 +111,11 @@ public void Handle_UnhandledResult_Ok() var called = false; var fallbackActionCalled = false; - _options.OnFallback = (_, _) => { called = true; return default; }; + _options.OnFallback = _ => { called = true; return default; }; _options.Handler.SetFallback(handler => { - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result == -1); - handler.FallbackAction = (_, _) => + handler.ShouldHandle = args => new ValueTask(args.Result == -1); + handler.FallbackAction = _ => { fallbackActionCalled = true; return new ValueTask(0); diff --git a/src/Polly.Core.Tests/Hedging/Controller/HedgingControllerTests.cs b/src/Polly.Core.Tests/Hedging/Controller/HedgingControllerTests.cs index fbbd4848c52..d445b36aaa4 100644 --- a/src/Polly.Core.Tests/Hedging/Controller/HedgingControllerTests.cs +++ b/src/Polly.Core.Tests/Hedging/Controller/HedgingControllerTests.cs @@ -12,7 +12,7 @@ public async Task Pooling_Ok() var handler = new HedgingHandler().SetHedging(handler => { handler.HedgingActionGenerator = args => null; - handler.ShouldHandle = (_, _) => PredicateResult.False; + handler.ShouldHandle = _ => PredicateResult.False; }).CreateHandler(); var controller = new HedgingController(new HedgingTimeProvider(), handler!, 3); diff --git a/src/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs b/src/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs index 4f8e97d87b4..6b9943a8ebb 100644 --- a/src/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs +++ b/src/Polly.Core.Tests/Hedging/Controller/HedgingExecutionContextTests.cs @@ -32,7 +32,7 @@ public HedgingExecutionContextTests() _hedgingHandler = new HedgingHandler(); _hedgingHandler.SetHedging(handler => { - handler.ShouldHandle = (outcome, _) => outcome switch + handler.ShouldHandle = args => args switch { { Exception: ApplicationException } => PredicateResult.True, { Result: DisposableResult result } when result.Name == Handled => PredicateResult.True, diff --git a/src/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs b/src/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs index 97ccb02f9e2..274fd35aefe 100644 --- a/src/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs +++ b/src/Polly.Core.Tests/Hedging/Controller/TaskExecutionTests.cs @@ -25,7 +25,7 @@ public TaskExecutionTests() _hedgingHandler = new HedgingHandler(); _hedgingHandler.SetHedging(handler => { - handler.ShouldHandle = (outcome, _) => outcome switch + handler.ShouldHandle = args => args switch { { Exception: ApplicationException } => PredicateResult.True, { Result: DisposableResult result } when result.Name == Handled => PredicateResult.True, diff --git a/src/Polly.Core.Tests/Hedging/HandleHedgingArgumentsTests.cs b/src/Polly.Core.Tests/Hedging/HandleHedgingArgumentsTests.cs index abb637adee7..ae31745ae9f 100644 --- a/src/Polly.Core.Tests/Hedging/HandleHedgingArgumentsTests.cs +++ b/src/Polly.Core.Tests/Hedging/HandleHedgingArgumentsTests.cs @@ -7,8 +7,6 @@ public class HandleHedgingArgumentsTests [Fact] public void Ctor_Ok() { - var args = new HandleHedgingArguments(ResilienceContext.Get()); - - args.Context.Should().NotBeNull(); + this.Invoking(_ => new HandleHedgingArguments()).Should().NotThrow(); } } diff --git a/src/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs b/src/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs index bb4eb521454..ae87a7afaa5 100644 --- a/src/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs +++ b/src/Polly.Core.Tests/Hedging/HedgingHandlerTests.cs @@ -56,12 +56,12 @@ public void SetHedging_Empty_Discarded() var handler = new HedgingHandler() .SetHedging(handler => { - handler.ShouldHandle = (_, _) => PredicateResult.True; + handler.ShouldHandle = _ => PredicateResult.True; handler.HedgingActionGenerator = args => () => Task.FromResult(10); }) .SetVoidHedging(handler => { - handler.ShouldHandle = (_, _) => PredicateResult.True; + handler.ShouldHandle = _ => PredicateResult.True; handler.HedgingActionGenerator = args => () => Task.CompletedTask; }); @@ -72,26 +72,27 @@ public void SetHedging_Empty_Discarded() [Fact] public async Task SetHedging_Ok() { + var context = ResilienceContext.Get(); var handler = new HedgingHandler() .SetHedging(handler => { handler.HedgingActionGenerator = args => () => Task.FromResult(0); - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result == -1); + handler.ShouldHandle = args => new ValueTask(args.Result == -1); }) .CreateHandler(); - var args = new HandleHedgingArguments(ResilienceContext.Get()); + var args = new HandleHedgingArguments(); handler.Should().NotBeNull(); - var result = await handler!.ShouldHandleAsync(new Outcome(-1), args); + var result = await handler!.ShouldHandleAsync(new(context, new Outcome(-1), args)); result.Should().BeTrue(); handler.HandlesHedging().Should().BeTrue(); - var action = handler.TryCreateHedgedAction(ResilienceContext.Get(), 0); + var action = handler.TryCreateHedgedAction(context, 0); action.Should().NotBeNull(); (await (action!()!)).Should().Be(0); - handler.TryCreateHedgedAction(ResilienceContext.Get(), 0).Should().BeNull(); + handler.TryCreateHedgedAction(context, 0).Should().BeNull(); } [InlineData(true)] @@ -99,6 +100,7 @@ public async Task SetHedging_Ok() [Theory] public async Task SetVoidHedging_Ok(bool returnsNullAction) { + var context = ResilienceContext.Get(); var handler = new HedgingHandler() .SetVoidHedging(handler => { @@ -112,13 +114,13 @@ public async Task SetVoidHedging_Ok(bool returnsNullAction) return () => Task.CompletedTask; }; - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException); + handler.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException); }) .CreateHandler(); - var args = new HandleHedgingArguments(ResilienceContext.Get()); + var args = new HandleHedgingArguments(); handler.Should().NotBeNull(); - var result = await handler!.ShouldHandleAsync(new Outcome(new InvalidOperationException()), args); + var result = await handler!.ShouldHandleAsync(new(context, new Outcome(new InvalidOperationException()), args)); result.Should().BeTrue(); handler.HandlesHedging().Should().BeTrue(); @@ -138,11 +140,12 @@ public async Task SetVoidHedging_Ok(bool returnsNullAction) [Fact] public async Task ShouldHandleAsync_UnknownResultType_Null() { + var context = ResilienceContext.Get(); var handler = new HedgingHandler() .SetHedging(handler => { handler.HedgingActionGenerator = args => () => Task.FromResult(0); - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException); + handler.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException); }) .SetHedging(handler => { @@ -152,12 +155,12 @@ public async Task ShouldHandleAsync_UnknownResultType_Null() return () => Task.FromResult("dummy"); }; - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Exception is InvalidOperationException); + handler.ShouldHandle = args => new ValueTask(args.Exception is InvalidOperationException); }) .CreateHandler(); - var args = new HandleHedgingArguments(ResilienceContext.Get()); - (await handler!.ShouldHandleAsync(new Outcome(new InvalidOperationException()), args)).Should().BeFalse(); + var args = new HandleHedgingArguments(); + (await handler!.ShouldHandleAsync(new(context, new Outcome(new InvalidOperationException()), args))).Should().BeFalse(); handler.HandlesHedging().Should().BeFalse(); handler.TryCreateHedgedAction(ResilienceContext.Get(), 0).Should().BeNull(); } diff --git a/src/Polly.Core.Tests/Hedging/HedgingResilienceStrategyBuilderExtensionsTests.cs b/src/Polly.Core.Tests/Hedging/HedgingResilienceStrategyBuilderExtensionsTests.cs index 88f36d35f5f..2076ce83767 100644 --- a/src/Polly.Core.Tests/Hedging/HedgingResilienceStrategyBuilderExtensionsTests.cs +++ b/src/Polly.Core.Tests/Hedging/HedgingResilienceStrategyBuilderExtensionsTests.cs @@ -24,7 +24,7 @@ public void AddHedging_Generic_Ok() _genericBuilder.AddHedging(new HedgingStrategyOptions { HedgingActionGenerator = args => () => Task.FromResult("dummy"), - ShouldHandle = (_, _) => PredicateResult.True + ShouldHandle = _ => PredicateResult.True }); _genericBuilder.Build().Strategy.Should().BeOfType(); } @@ -67,7 +67,7 @@ public async Task AddHedging_IntegrationTest() HedgingDelay = TimeSpan.FromMilliseconds(20), Handler = new HedgingHandler().SetHedging(handler => { - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result == "error"); + handler.ShouldHandle = args => new ValueTask(args.Result == "error"); handler.HedgingActionGenerator = args => { return async () => @@ -83,7 +83,7 @@ public async Task AddHedging_IntegrationTest() }; }; }), - OnHedging = (outcome, _) => { results.Enqueue(outcome.Result!.ToString()!); return default; } + OnHedging = args => { results.Enqueue(args.Result!.ToString()!); return default; } }) .Build(); diff --git a/src/Polly.Core.Tests/Hedging/HedgingResilienceStrategyTests.cs b/src/Polly.Core.Tests/Hedging/HedgingResilienceStrategyTests.cs index 8082ddc8098..60857f63190 100644 --- a/src/Polly.Core.Tests/Hedging/HedgingResilienceStrategyTests.cs +++ b/src/Polly.Core.Tests/Hedging/HedgingResilienceStrategyTests.cs @@ -15,7 +15,7 @@ public class HedgingResilienceStrategyTests : IDisposable private static readonly TimeSpan AssertTimeout = TimeSpan.FromSeconds(15); private readonly HedgingStrategyOptions _options = new(); - private readonly List _events = new(); + private readonly List _events = new(); private readonly ResilienceStrategyTelemetry _telemetry; private readonly HedgingTimeProvider _timeProvider; private readonly HedgingActions _actions; @@ -217,7 +217,7 @@ public async Task ExecuteAsync_EnsureDiscardedResultDisposed() }; }; - handler.ShouldHandle = (_, _) => PredicateResult.False; + handler.ShouldHandle = _ => PredicateResult.False; }); var strategy = Create(); @@ -268,7 +268,7 @@ public async Task ExecuteAsync_EveryHedgedTaskShouldHaveDifferentContexts() }; }; - handler.ShouldHandle = (_, _) => PredicateResult.False; + handler.ShouldHandle = _ => PredicateResult.False; }); var strategy = Create(); @@ -443,7 +443,7 @@ public async Task ExecuteAsync_OnHedgingEventThrows_EnsureExceptionRethrown() { // arrange ConfigureHedging(args => () => Task.FromResult(Success)); - _options.OnHedging = (_, _) => throw new InvalidOperationException("my-exception"); + _options.OnHedging = _ => throw new InvalidOperationException("my-exception"); var strategy = Create(); // act @@ -625,7 +625,7 @@ public async Task ExecuteAsync_ExceptionsHandled_ShouldThrowAnyException() ConfigureHedging(handler => { - handler.ShouldHandle = (outcome, _) => outcome.Exception switch + handler.ShouldHandle = args => args.Exception switch { InvalidOperationException => PredicateResult.True, ArgumentException => PredicateResult.True, @@ -659,7 +659,7 @@ public async Task ExecuteAsync_ExceptionsHandled_ShouldThrowLastException() ConfigureHedging(handler => { - handler.ShouldHandle = (outcome, _) => outcome.Exception switch + handler.ShouldHandle = args => args.Exception switch { InvalidOperationException => PredicateResult.True, ArgumentException => PredicateResult.True, @@ -695,7 +695,7 @@ public async Task ExecuteAsync_PrimaryExceptionNotHandled_Rethrow() ConfigureHedging(handler => { - handler.ShouldHandle = (outcome, _) => outcome.Exception switch + handler.ShouldHandle = args => args.Exception switch { InvalidOperationException => PredicateResult.True, _ => PredicateResult.False @@ -720,7 +720,7 @@ public async Task ExecuteAsync_ExceptionsHandled_ShouldReturnLastResult() ConfigureHedging(handler => { - handler.ShouldHandle = (outcome, _) => outcome.Exception switch + handler.ShouldHandle = args => args.Exception switch { InvalidOperationException => PredicateResult.True, ArgumentException => PredicateResult.True, @@ -766,7 +766,7 @@ public async Task ExecuteAsync_EnsureHedgingDelayGeneratorRespected() ConfigureHedging(handler => { handler.HedgingActionGenerator = args => () => Task.FromResult(Success); - handler.ShouldHandle = (_, _) => PredicateResult.False; + handler.ShouldHandle = _ => PredicateResult.False; }); var strategy = Create(); @@ -787,7 +787,7 @@ public async Task ExecuteAsync_EnsureExceptionStackTracePreserved() ConfigureHedging(handler => { handler.HedgingActionGenerator = args => null; - handler.ShouldHandle = (_, _) => PredicateResult.False; + handler.ShouldHandle = _ => PredicateResult.False; }); var strategy = Create(); @@ -804,16 +804,16 @@ public async Task ExecuteAsync_EnsureExceptionStackTracePreserved() public async Task ExecuteAsync_EnsureOnHedgingCalled() { var attempts = new List(); - _options.OnHedging = (o, args) => + _options.OnHedging = args => { - o.Result.Should().Be(Failure); - attempts.Add(args.Attempt); + args.Result.Should().Be(Failure); + attempts.Add(args.Arguments.Attempt); return default; }; ConfigureHedging(handler => { - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result == Failure); + handler.ShouldHandle = args => new ValueTask(args.Result == Failure); handler.HedgingActionGenerator = args => () => Task.FromResult(Failure); }); @@ -832,7 +832,7 @@ public async Task ExecuteAsync_EnsureOnHedgingTelemetry() ConfigureHedging(handler => { - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result == Failure); + handler.ShouldHandle = args => new ValueTask(args.Result == Failure); handler.HedgingActionGenerator = args => () => Task.FromResult(Failure); }); @@ -845,11 +845,11 @@ public async Task ExecuteAsync_EnsureOnHedgingTelemetry() private void ConfigureHedging() { - _options.OnHedging = (outcome, _) => + _options.OnHedging = args => { lock (_results) { - _results.Add(outcome.Result!); + _results.Add(args.Result!); } return default; @@ -858,7 +858,7 @@ private void ConfigureHedging() ConfigureHedging(handler => { handler.HedgingActionGenerator = _actions.Generator; - handler.ShouldHandle = (_, _) => PredicateResult.False; + handler.ShouldHandle = _ => PredicateResult.False; }); } @@ -872,7 +872,7 @@ private void ConfigureHedging(Func, Func ConfigureHedging(handler => { handler.HedgingActionGenerator = generator; - handler.ShouldHandle = (outcome, _) => new ValueTask(outcome.Result == Failure); + handler.ShouldHandle = args => new ValueTask(args.Result == Failure); }); } diff --git a/src/Polly.Core.Tests/Issues/IssuesTests.CircuitBreakerStateSharing_959.cs b/src/Polly.Core.Tests/Issues/IssuesTests.CircuitBreakerStateSharing_959.cs index 7e7ecd7c91e..77e7a827bd4 100644 --- a/src/Polly.Core.Tests/Issues/IssuesTests.CircuitBreakerStateSharing_959.cs +++ b/src/Polly.Core.Tests/Issues/IssuesTests.CircuitBreakerStateSharing_959.cs @@ -11,7 +11,7 @@ public void CircuitBreakerStateSharing_959() { FailureThreshold = 1, MinimumThroughput = 10, - ShouldHandle = (outcome, _) => outcome.Result switch + ShouldHandle = args => args.Result switch { // handle int results int intVal when intVal == -1 => new ValueTask(true), diff --git a/src/Polly.Core.Tests/Issues/IssuesTests.FlowingContext_849.cs b/src/Polly.Core.Tests/Issues/IssuesTests.FlowingContext_849.cs index 1fcbb2bfb08..d282bf1c9b2 100644 --- a/src/Polly.Core.Tests/Issues/IssuesTests.FlowingContext_849.cs +++ b/src/Polly.Core.Tests/Issues/IssuesTests.FlowingContext_849.cs @@ -13,7 +13,7 @@ public void FlowingContext_849() .AddRetry(new RetryStrategyOptions { // configure the predicate and use the context - ShouldRetry = (_, args) => + ShouldRetry = args => { // access the context to evaluate the retry ResilienceContext context = args.Context; diff --git a/src/Polly.Core.Tests/Issues/IssuesTests.HandleMultipleResults_898.cs b/src/Polly.Core.Tests/Issues/IssuesTests.HandleMultipleResults_898.cs index 0bbebb8e93c..e96c35dd7db 100644 --- a/src/Polly.Core.Tests/Issues/IssuesTests.HandleMultipleResults_898.cs +++ b/src/Polly.Core.Tests/Issues/IssuesTests.HandleMultipleResults_898.cs @@ -14,7 +14,7 @@ public void HandleMultipleResults_898() BackoffType = RetryBackoffType.Constant, RetryCount = 1, BaseDelay = TimeSpan.FromMilliseconds(1), - ShouldRetry = (outcome, _) => outcome switch + ShouldRetry = args => args switch { // handle string results { Result: string res } when res == "error" => PredicateResult.True, @@ -23,7 +23,7 @@ public void HandleMultipleResults_898() { Result: int res } when res == -1 => PredicateResult.True, _ => PredicateResult.False }, - OnRetry = (_, args) => + OnRetry = args => { // add a callback updates the resilience context with the retry marker args.Context.Properties.Set(isRetryKey, true); diff --git a/src/Polly.Core.Tests/Retry/RetryDelayArgumentsTests.cs b/src/Polly.Core.Tests/Retry/RetryDelayArgumentsTests.cs index 84dc24bf6f3..3dc9e50dce1 100644 --- a/src/Polly.Core.Tests/Retry/RetryDelayArgumentsTests.cs +++ b/src/Polly.Core.Tests/Retry/RetryDelayArgumentsTests.cs @@ -7,9 +7,8 @@ public class RetryDelayArgumentsTests [Fact] public void Ctor_Ok() { - var args = new RetryDelayArguments(ResilienceContext.Get(), 2, TimeSpan.FromSeconds(2)); + var args = new RetryDelayArguments(2, TimeSpan.FromSeconds(2)); - args.Context.Should().NotBeNull(); args.Attempt.Should().Be(2); args.DelayHint.Should().Be(TimeSpan.FromSeconds(2)); } diff --git a/src/Polly.Core.Tests/Retry/RetryResilienceStrategyBuilderExtensionsTests.cs b/src/Polly.Core.Tests/Retry/RetryResilienceStrategyBuilderExtensionsTests.cs index 10892181a2d..ef96d7c6e4f 100644 --- a/src/Polly.Core.Tests/Retry/RetryResilienceStrategyBuilderExtensionsTests.cs +++ b/src/Polly.Core.Tests/Retry/RetryResilienceStrategyBuilderExtensionsTests.cs @@ -17,7 +17,7 @@ public class RetryResilienceStrategyBuilderExtensionsTests BackoffType = RetryBackoffType.Exponential, RetryCount = 3, BaseDelay = TimeSpan.FromSeconds(2), - ShouldRetry = (_, _) => PredicateResult.True, + ShouldRetry = _ => PredicateResult.True, }); AssertStrategy(builder, RetryBackoffType.Exponential, 3, TimeSpan.FromSeconds(2)); @@ -47,9 +47,10 @@ public class RetryResilienceStrategyBuilderExtensionsTests AssertStrategy(builder, RetryBackoffType.ExponentialWithJitter, 3, TimeSpan.FromSeconds(2), strategy => { - var args = new RetryDelayArguments(ResilienceContext.Get().Initialize(true), 8, TimeSpan.Zero); + var args = new RetryDelayArguments(8, TimeSpan.Zero); + var context = ResilienceContext.Get().Initialize(true); - strategy.DelayGenerator!.HandleAsync(new Outcome(new InvalidOperationException()), args).Result.Should().Be(TimeSpan.FromMilliseconds(8)); + strategy.DelayGenerator!.HandleAsync(new(context, new Outcome(new InvalidOperationException()), args)).Result.Should().Be(TimeSpan.FromMilliseconds(8)); }); }, builder => @@ -59,7 +60,7 @@ public class RetryResilienceStrategyBuilderExtensionsTests BackoffType = RetryBackoffType.Exponential, RetryCount = 3, BaseDelay = TimeSpan.FromSeconds(2), - ShouldRetry = (_, _) => PredicateResult.True + ShouldRetry = _ => PredicateResult.True }); AssertStrategy(builder, RetryBackoffType.Exponential, 3, TimeSpan.FromSeconds(2)); @@ -88,7 +89,7 @@ public void AddRetry_GenericOverloads_Ok(Action> public void AddRetry_DefaultOptions_Ok() { var builder = new ResilienceStrategyBuilder(); - var options = new RetryStrategyOptions { ShouldRetry = (_, _) => PredicateResult.True }; + var options = new RetryStrategyOptions { ShouldRetry = _ => PredicateResult.True }; builder.AddRetry(options); diff --git a/src/Polly.Core.Tests/Retry/RetryResilienceStrategyTests.cs b/src/Polly.Core.Tests/Retry/RetryResilienceStrategyTests.cs index 49a8c5a6aac..f49014c0cbb 100644 --- a/src/Polly.Core.Tests/Retry/RetryResilienceStrategyTests.cs +++ b/src/Polly.Core.Tests/Retry/RetryResilienceStrategyTests.cs @@ -15,7 +15,7 @@ public class RetryResilienceStrategyTests public RetryResilienceStrategyTests() { _telemetry = TestUtilities.CreateResilienceTelemetry(_diagnosticSource.Object); - _options.ShouldRetry = (_, _) => new ValueTask(false); + _options.ShouldRetry = _ => new ValueTask(false); } [Fact] @@ -35,7 +35,7 @@ public void ExecuteAsync_MultipleRetries_EnsureDiscardedResultsDisposed() _options.RetryCount = 5; SetupNoDelay(); _timeProvider.SetupAnyDelay(); - _options.ShouldRetry = (_, _) => PredicateResult.True; + _options.ShouldRetry = _ => PredicateResult.True; var results = new List(); var sut = CreateSut(); @@ -60,8 +60,8 @@ public void ExecuteAsync_MultipleRetries_EnsureDiscardedResultsDisposed() public void Retry_RetryCount_Respected() { int calls = 0; - _options.OnRetry = (_, _) => { calls++; return default; }; - _options.ShouldRetry = (outcome, _) => outcome.ResultPredicateAsync(0); + _options.OnRetry = _ => { calls++; return default; }; + _options.ShouldRetry = args => args.Outcome.ResultPredicateAsync(0); _options.RetryCount = 12; SetupNoDelay(); var sut = CreateSut(); @@ -75,14 +75,14 @@ public void Retry_RetryCount_Respected() public void RetryException_RetryCount_Respected() { int calls = 0; - _options.OnRetry = (outcome, _) => + _options.OnRetry = args => { - outcome.Exception.Should().BeOfType(); + args.Exception.Should().BeOfType(); calls++; return default; }; - _options.ShouldRetry = (outcome, _) => outcome.ExceptionPredicateAsync(); + _options.ShouldRetry = args => args.Outcome.ExceptionPredicateAsync(); _options.RetryCount = 3; SetupNoDelay(); var sut = CreateSut(); @@ -97,9 +97,9 @@ public void Retry_Infinite_Respected() { int calls = 0; _options.BackoffType = RetryBackoffType.Constant; - _options.OnRetry = (_, args) => + _options.OnRetry = args => { - if (args.Attempt > RetryConstants.MaxRetryCount) + if (args.Arguments.Attempt > RetryConstants.MaxRetryCount) { throw new InvalidOperationException(); } @@ -107,7 +107,7 @@ public void Retry_Infinite_Respected() calls++; return default; }; - _options.ShouldRetry = (outcome, _) => outcome.ResultPredicateAsync(0); + _options.ShouldRetry = args => args.Outcome.ResultPredicateAsync(0); _options.RetryCount = RetryStrategyOptions.InfiniteRetryCount; SetupNoDelay(); var sut = CreateSut(); @@ -121,11 +121,11 @@ public void Retry_Infinite_Respected() public void RetryDelayGenerator_Respected() { int calls = 0; - _options.OnRetry = (_, _) => { calls++; return default; }; - _options.ShouldRetry = (outcome, _) => outcome.ResultPredicateAsync(0); + _options.OnRetry = _ => { calls++; return default; }; + _options.ShouldRetry = args => args.Outcome.ResultPredicateAsync(0); _options.RetryCount = 3; _options.BackoffType = RetryBackoffType.Constant; - _options.RetryDelayGenerator = (_, _) => new ValueTask(TimeSpan.FromMilliseconds(123)); + _options.RetryDelayGenerator = _ => new ValueTask(TimeSpan.FromMilliseconds(123)); _timeProvider.SetupDelay(TimeSpan.FromMilliseconds(123)); var sut = CreateSut(); @@ -140,17 +140,17 @@ public void OnRetry_EnsureCorrectArguments() { var attempts = new List(); var delays = new List(); - _options.OnRetry = (outcome, args) => + _options.OnRetry = args => { - attempts.Add(args.Attempt); - delays.Add(args.RetryDelay); + attempts.Add(args.Arguments.Attempt); + delays.Add(args.Arguments.RetryDelay); - outcome.Exception.Should().BeNull(); - outcome.Result.Should().Be(0); + args.Exception.Should().BeNull(); + args.Result.Should().Be(0); return default; }; - _options.ShouldRetry = (outcome, _) => outcome.ResultPredicateAsync(0); + _options.ShouldRetry = args => args.Outcome.ResultPredicateAsync(0); _options.RetryCount = 3; _options.BackoffType = RetryBackoffType.Linear; _timeProvider.SetupAnyDelay(); @@ -177,7 +177,7 @@ public void OnRetry_EnsureTelemetry() _diagnosticSource.Setup(v => v.IsEnabled("OnRetry")).Returns(true); - _options.ShouldRetry = (outcome, _) => outcome.ResultPredicateAsync(0); + _options.ShouldRetry = args => args.Outcome.ResultPredicateAsync(0); _options.RetryCount = 3; _options.BackoffType = RetryBackoffType.Linear; _timeProvider.SetupAnyDelay(); @@ -194,18 +194,18 @@ public void RetryDelayGenerator_EnsureCorrectArguments() { var attempts = new List(); var hints = new List(); - _options.RetryDelayGenerator = (outcome, args) => + _options.RetryDelayGenerator = args => { - attempts.Add(args.Attempt); - hints.Add(args.DelayHint); + attempts.Add(args.Arguments.Attempt); + hints.Add(args.Arguments.DelayHint); - outcome.Exception.Should().BeNull(); - outcome.Result.Should().Be(0); + args.Exception.Should().BeNull(); + args.Result.Should().Be(0); return new ValueTask(TimeSpan.Zero); }; - _options.ShouldRetry = (outcome, _) => outcome.ResultPredicateAsync(0); + _options.ShouldRetry = args => args.Outcome.ResultPredicateAsync(0); _options.RetryCount = 3; _options.BackoffType = RetryBackoffType.Linear; _timeProvider.SetupAnyDelay(); @@ -224,7 +224,7 @@ public void RetryDelayGenerator_EnsureCorrectArguments() hints[2].Should().Be(TimeSpan.FromSeconds(6)); } - private void SetupNoDelay() => _options.RetryDelayGenerator = (_, _) => new ValueTask(TimeSpan.Zero); + private void SetupNoDelay() => _options.RetryDelayGenerator = _ => new ValueTask(TimeSpan.Zero); private RetryResilienceStrategy CreateSut() { diff --git a/src/Polly.Core.Tests/Retry/ShouldRetryArgumentsTests.cs b/src/Polly.Core.Tests/Retry/ShouldRetryArgumentsTests.cs index 50ce4c28d0c..5aef531e67f 100644 --- a/src/Polly.Core.Tests/Retry/ShouldRetryArgumentsTests.cs +++ b/src/Polly.Core.Tests/Retry/ShouldRetryArgumentsTests.cs @@ -7,9 +7,7 @@ public class ShouldRetryArgumentsTests [Fact] public void Ctor_Ok() { - var args = new ShouldRetryArguments(ResilienceContext.Get(), 2); - - args.Context.Should().NotBeNull(); + var args = new ShouldRetryArguments(2); args.Attempt.Should().Be(2); } } diff --git a/src/Polly.Core.Tests/Strategy/EventInvokerTests.cs b/src/Polly.Core.Tests/Strategy/EventInvokerTests.cs index a76e7512647..61b55777d86 100644 --- a/src/Polly.Core.Tests/Strategy/EventInvokerTests.cs +++ b/src/Polly.Core.Tests/Strategy/EventInvokerTests.cs @@ -16,53 +16,56 @@ public void NullCallback_Ok() [Fact] public async Task HandleAsync_NonGeneric_Ok() { - var args = new TestArguments(ResilienceContext.Get()); + var context = ResilienceContext.Get(); + var args = new TestArguments(); var called = false; - var invoker = EventInvoker.Create((outcome, args) => + var invoker = EventInvoker.Create(args => { - outcome.Result.Should().Be(10); + args.Result.Should().Be(10); args.Context.Should().NotBeNull(); called = true; return default; }, false)!; - await invoker.HandleAsync(new Outcome(10), args); + await invoker.HandleAsync(new(context, new Outcome(10), args)); called.Should().Be(true); } [Fact] public async Task HandleAsync_Generic_Ok() { - var args = new TestArguments(ResilienceContext.Get()); + var context = ResilienceContext.Get(); + var args = new TestArguments(); var called = false; - var invoker = EventInvoker.Create((outcome, args) => + var invoker = EventInvoker.Create(args => { args.Context.Should().NotBeNull(); - outcome.Result.Should().Be(10); + args.Result.Should().Be(10); called = true; return default; }, true)!; - await invoker.HandleAsync(new Outcome(10), args); + await invoker.HandleAsync(new(context, new Outcome(10), args)); called.Should().Be(true); called = false; - await invoker.HandleAsync(new Outcome("dummy"), args); + await invoker.HandleAsync(new(ResilienceContext.Get(), new Outcome("dummy"), args)); called.Should().Be(false); } [Fact] public async Task HandleAsync_GenericObject_Ok() { + var context = ResilienceContext.Get(); var called = false; - var args = new TestArguments(ResilienceContext.Get()); - var invoker = EventInvoker.Create((_, _) => { called = true; return default; }, true); - await invoker!.HandleAsync(new Outcome("dummy"), args); + var args = new TestArguments(); + var invoker = EventInvoker.Create(_ => { called = true; return default; }, true); + await invoker!.HandleAsync(new(context, new Outcome("dummy"), args)); called.Should().BeFalse(); - await invoker!.HandleAsync(new Outcome("dummy"), args); + await invoker!.HandleAsync(new(context, new Outcome("dummy"), args)); called.Should().BeTrue(); } } diff --git a/src/Polly.Core.Tests/Strategy/GeneratorInvokerTests.cs b/src/Polly.Core.Tests/Strategy/GeneratorInvokerTests.cs index 5009ee9ff5f..91f2aeb40ef 100644 --- a/src/Polly.Core.Tests/Strategy/GeneratorInvokerTests.cs +++ b/src/Polly.Core.Tests/Strategy/GeneratorInvokerTests.cs @@ -15,48 +15,53 @@ public void NullCallback_Ok() [Fact] public async Task HandleAsync_NonGeneric_Ok() { - var args = new TestArguments(ResilienceContext.Get()); - var invoker = GeneratorInvoker.Create((outcome, args) => + var context = ResilienceContext.Get(); + var args = new TestArguments(); + var invoker = GeneratorInvoker.Create(args => { args.Context.Should().NotBeNull(); - outcome.Result.Should().Be(10); + args.Result.Should().Be(10); return new ValueTask("generated-value"); }, "default", false)!; - (await invoker.HandleAsync(new Outcome(10), args)).Should().Be("generated-value"); + var outcomeArgs = new OutcomeArguments(context, new Outcome(10), args); + (await invoker.HandleAsync(outcomeArgs)).Should().Be("generated-value"); } [Fact] public async Task HandleAsync_Generic_Ok() { - var args = new TestArguments(ResilienceContext.Get()); - var invoker = GeneratorInvoker.Create((outcome, args) => + var context = ResilienceContext.Get(); + var args = new TestArguments(); + var invoker = GeneratorInvoker.Create(args => { args.Context.Should().NotBeNull(); - outcome.Result.Should().Be(10); + args.Result.Should().Be(10); return new ValueTask("generated-value"); }, "default", true)!; - (await invoker.HandleAsync(new Outcome(10), args)).Should().Be("generated-value"); - (await invoker.HandleAsync(new Outcome("dummy"), args)).Should().Be("default"); + (await invoker.HandleAsync(new(context, new Outcome(10), args))).Should().Be("generated-value"); + (await invoker.HandleAsync(new(context, new Outcome("dummy"), args))).Should().Be("default"); - invoker = GeneratorInvoker.Create((_, _) => new ValueTask("dummy"), "default", true); - (await invoker!.HandleAsync(new Outcome("dummy"), args)).Should().Be("default"); - (await invoker!.HandleAsync(new Outcome("dummy"), args)).Should().Be("dummy"); + invoker = GeneratorInvoker.Create(_ => new ValueTask("dummy"), "default", true); + (await invoker!.HandleAsync(new(context, new Outcome("dummy"), args))).Should().Be("default"); + (await invoker!.HandleAsync(new(context, new Outcome("dummy"), args))).Should().Be("dummy"); } [Fact] public async Task HandleAsync_GenericObject_Ok() { - var args = new TestArguments(ResilienceContext.Get()); - var invoker = GeneratorInvoker.Create((_, _) => new ValueTask("dummy"), "default", true); - (await invoker!.HandleAsync(new Outcome("dummy"), args)).Should().Be("default"); - (await invoker!.HandleAsync(new Outcome("dummy"), args)).Should().Be("dummy"); + var context = ResilienceContext.Get(); + + var args = new TestArguments(); + var invoker = GeneratorInvoker.Create(_ => new ValueTask("dummy"), "default", true); + (await invoker!.HandleAsync(new(context, new Outcome("dummy"), args))).Should().Be("default"); + (await invoker!.HandleAsync(new(context, new Outcome("dummy"), args))).Should().Be("dummy"); } } diff --git a/src/Polly.Core.Tests/Strategy/OnRetryArgumentsTests.cs b/src/Polly.Core.Tests/Strategy/OnRetryArgumentsTests.cs index aa57f6a9a0c..e673deebd68 100644 --- a/src/Polly.Core.Tests/Strategy/OnRetryArgumentsTests.cs +++ b/src/Polly.Core.Tests/Strategy/OnRetryArgumentsTests.cs @@ -7,9 +7,8 @@ public class OnRetryArgumentsTests [Fact] public void Ctor_Ok() { - var args = new OnRetryArguments(ResilienceContext.Get(), 2, TimeSpan.FromSeconds(3)); + var args = new OnRetryArguments(2, TimeSpan.FromSeconds(3)); - args.Context.Should().NotBeNull(); args.Attempt.Should().Be(2); args.RetryDelay.Should().Be(TimeSpan.FromSeconds(3)); } diff --git a/src/Polly.Core.Tests/Strategy/OutcomeArgumentsTests.cs b/src/Polly.Core.Tests/Strategy/OutcomeArgumentsTests.cs new file mode 100644 index 00000000000..1823ca32282 --- /dev/null +++ b/src/Polly.Core.Tests/Strategy/OutcomeArgumentsTests.cs @@ -0,0 +1,35 @@ +using Polly.Strategy; + +namespace Polly.Core.Tests.Strategy; + +public class OutcomeArgumentsTests +{ + [Fact] + public void Ctor_Result_Ok() + { + var args = new OutcomeArguments( + ResilienceContext.Get(), + new Outcome("dummy"), + "args"); + + args.Context.Should().NotBeNull(); + args.Exception.Should().BeNull(); + args.Outcome.Result.Should().Be("dummy"); + args.Arguments.Should().Be("args"); + args.Result.Should().Be("dummy"); + } + + [Fact] + public void Ctor_Exception_Ok() + { + var args = new OutcomeArguments( + ResilienceContext.Get(), + new Outcome(new InvalidOperationException()), + "args"); + + args.Context.Should().NotBeNull(); + args.Exception.Should().BeOfType(); + args.Arguments.Should().Be("args"); + args.Result.Should().BeNull(); + } +} diff --git a/src/Polly.Core.Tests/Strategy/PredicateBuilderTests.cs b/src/Polly.Core.Tests/Strategy/PredicateBuilderTests.cs index ea3b235e4e6..fe5c8aa53a7 100644 --- a/src/Polly.Core.Tests/Strategy/PredicateBuilderTests.cs +++ b/src/Polly.Core.Tests/Strategy/PredicateBuilderTests.cs @@ -36,7 +36,7 @@ public async Task HandleResult_Ok(Action> configure, Ou configure(predicate); - var result = await predicate.CreatePredicate()(value, string.Empty); + var result = await predicate.CreatePredicate()(new OutcomeArguments(ResilienceContext.Get(), value, string.Empty)); result.Should().Be(handled); } diff --git a/src/Polly.Core.Tests/Strategy/PredicateInvokerTests.cs b/src/Polly.Core.Tests/Strategy/PredicateInvokerTests.cs index 1d39b7e8331..b5abc3b8346 100644 --- a/src/Polly.Core.Tests/Strategy/PredicateInvokerTests.cs +++ b/src/Polly.Core.Tests/Strategy/PredicateInvokerTests.cs @@ -16,49 +16,52 @@ public void NullCallback_Ok() [Fact] public async Task HandleAsync_NonGeneric_Ok() { - var args = new TestArguments(ResilienceContext.Get()); + var context = ResilienceContext.Get(); + var args = new TestArguments(); var called = false; - var invoker = PredicateInvoker.Create((outcome, _) => + var invoker = PredicateInvoker.Create(args => { - outcome.Result.Should().Be(10); + args.Result.Should().Be(10); args.Context.Should().NotBeNull(); called = true; return new ValueTask(true); }, false); - (await invoker!.HandleAsync(new Outcome(10), args)).Should().Be(true); + (await invoker!.HandleAsync(new(context, new Outcome(10), args))).Should().Be(true); called.Should().Be(true); } [Fact] public async Task HandleAsync_Generic_Ok() { - var args = new TestArguments(ResilienceContext.Get()); + var context = ResilienceContext.Get(); + var args = new TestArguments(); var called = false; - var invoker = PredicateInvoker.Create((outcome, args) => + var invoker = PredicateInvoker.Create(args => { - outcome.Result.Should().Be(10); + args.Result.Should().Be(10); args.Context.Should().NotBeNull(); called = true; return new ValueTask(true); }, true); - (await invoker!.HandleAsync(new Outcome(10), args)).Should().Be(true); + (await invoker!.HandleAsync(new(context, new Outcome(10), args))).Should().Be(true); called.Should().Be(true); called = false; - (await invoker.HandleAsync(new Outcome("dummy"), args)).Should().Be(false); + (await invoker.HandleAsync(new(context, new Outcome("dummy"), args))).Should().Be(false); called.Should().Be(false); } [Fact] public async Task HandleAsync_GenericObject_Ok() { - var args = new TestArguments(ResilienceContext.Get()); - var invoker = PredicateInvoker.Create((_, _) => PredicateResult.True, true); - (await invoker!.HandleAsync(new Outcome("dummy"), args)).Should().BeFalse(); - (await invoker!.HandleAsync(new Outcome("dummy"), args)).Should().BeTrue(); + var context = ResilienceContext.Get(); + var args = new TestArguments(); + var invoker = PredicateInvoker.Create(_ => PredicateResult.True, true); + (await invoker!.HandleAsync(new(context, new Outcome("dummy"), args))).Should().BeFalse(); + (await invoker!.HandleAsync(new(context, new Outcome("dummy"), args))).Should().BeTrue(); } } diff --git a/src/Polly.Core.Tests/Strategy/ResilienceStrategyTelemetryTests.cs b/src/Polly.Core.Tests/Strategy/ResilienceStrategyTelemetryTests.cs index 494aabc51f1..a5f6d79f991 100644 --- a/src/Polly.Core.Tests/Strategy/ResilienceStrategyTelemetryTests.cs +++ b/src/Polly.Core.Tests/Strategy/ResilienceStrategyTelemetryTests.cs @@ -34,7 +34,7 @@ public void Report_NoOutcome_OK() args.Context.Should().NotBeNull(); }); - _sut.Report("dummy-event", new TestArguments()); + _sut.Report("dummy-event", ResilienceContext.Get(), new TestArguments()); _diagnosticSource.VerifyAll(); } @@ -44,7 +44,7 @@ public void Report_NoOutcomeWhenNotSubscribed_None() { _diagnosticSource.Setup(o => o.IsEnabled("dummy-event")).Returns(false); - _sut.Report("dummy-event", new TestArguments()); + _sut.Report("dummy-event", ResilienceContext.Get(), new TestArguments()); _diagnosticSource.VerifyAll(); _diagnosticSource.VerifyNoOtherCalls(); @@ -55,9 +55,10 @@ public void ResilienceStrategyTelemetry_NoDiagnosticSource_Ok() { var source = new ResilienceTelemetrySource("builder", new ResilienceProperties(), "strategy-name", "strategy-type"); var sut = new ResilienceStrategyTelemetry(source, null); + var context = ResilienceContext.Get(); - sut.Invoking(s => s.Report("dummy", new TestArguments())).Should().NotThrow(); - sut.Invoking(s => s.Report("dummy", new Outcome(1), new TestArguments())).Should().NotThrow(); + sut.Invoking(s => s.Report("dummy", context, new TestArguments())).Should().NotThrow(); + sut.Invoking(s => s.Report("dummy", new OutcomeArguments(context, new Outcome(1), new TestArguments()))).Should().NotThrow(); } [Fact] @@ -82,7 +83,8 @@ public void Report_Outcome_OK() args.Context.Should().NotBeNull(); }); - _sut.Report("dummy-event", new Outcome(99), new TestArguments()); + var context = ResilienceContext.Get(); + _sut.Report("dummy-event", new OutcomeArguments(context, new Outcome(99), new TestArguments())); _diagnosticSource.VerifyAll(); } @@ -91,8 +93,8 @@ public void Report_Outcome_OK() public void Report_OutcomeWhenNotSubscribed_None() { _diagnosticSource.Setup(o => o.IsEnabled("dummy-event")).Returns(false); - - _sut.Report("dummy-event", new Outcome(10), new TestArguments()); + var context = ResilienceContext.Get(); + _sut.Report("dummy-event", new OutcomeArguments(context, new Outcome(10), new TestArguments())); _diagnosticSource.VerifyAll(); _diagnosticSource.VerifyNoOtherCalls(); diff --git a/src/Polly.Core/CircuitBreaker/CircuitBreakerPredicateArguments.cs b/src/Polly.Core/CircuitBreaker/CircuitBreakerPredicateArguments.cs index b5d43563525..7d6ab6777de 100644 --- a/src/Polly.Core/CircuitBreaker/CircuitBreakerPredicateArguments.cs +++ b/src/Polly.Core/CircuitBreaker/CircuitBreakerPredicateArguments.cs @@ -1,9 +1,6 @@ -using Polly.Strategy; - namespace Polly.CircuitBreaker; /// /// Arguments used by predicate. /// -/// The context associated with the execution of a user-provided callback. -public readonly record struct CircuitBreakerPredicateArguments(ResilienceContext Context) : IResilienceArguments; +public readonly record struct CircuitBreakerPredicateArguments(); diff --git a/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategy.cs b/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategy.cs index 8b4ad93091f..8a9cf19a642 100644 --- a/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategy.cs +++ b/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategy.cs @@ -35,8 +35,8 @@ protected internal override async ValueTask> ExecuteCoreAsync(context, outcome, new CircuitBreakerPredicateArguments()); + if (await _handler.HandleAsync(args).ConfigureAwait(context.ContinueOnCapturedContext)) { await _controller.OnActionFailureAsync(outcome, context).ConfigureAwait(context.ContinueOnCapturedContext); } diff --git a/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.cs b/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.cs index 98e1b62d79c..f7fa3c97924 100644 --- a/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.cs +++ b/src/Polly.Core/CircuitBreaker/CircuitBreakerStrategyOptions.cs @@ -41,7 +41,7 @@ public abstract class CircuitBreakerStrategyOptions : ResilienceStrateg /// Defaults to . This property is required. /// [Required] - public Func, CircuitBreakerPredicateArguments, ValueTask>? ShouldHandle { get; set; } + public Func, ValueTask>? ShouldHandle { get; set; } /// /// Gets or sets the event that is raised when the circuit resets to a state. @@ -59,7 +59,7 @@ public abstract class CircuitBreakerStrategyOptions : ResilienceStrateg /// Defaults to . /// /// - public Func, OnCircuitClosedArguments, ValueTask>? OnClosed { get; set; } + public Func, ValueTask>? OnClosed { get; set; } /// /// Gets or sets the event that is raised when the circuit transitions to an state. @@ -77,7 +77,7 @@ public abstract class CircuitBreakerStrategyOptions : ResilienceStrateg /// Defaults to . /// /// - public Func, OnCircuitOpenedArguments, ValueTask>? OnOpened { get; set; } + public Func, ValueTask>? OnOpened { get; set; } /// /// Gets or sets the event that is raised when when the circuit transitions to an state. diff --git a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs index c36b2f5b864..7a9b04992bc 100644 --- a/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs +++ b/src/Polly.Core/CircuitBreaker/Controller/CircuitStateController.cs @@ -129,7 +129,7 @@ public ValueTask CloseCircuitAsync(ResilienceContext context) if (_circuitState == CircuitState.Open && PermitHalfOpenCircuitTest_NeedsLock()) { _circuitState = CircuitState.HalfOpen; - _telemetry.Report(CircuitBreakerConstants.OnHalfOpenEvent, new OnCircuitHalfOpenedArguments(context)); + _telemetry.Report(CircuitBreakerConstants.OnHalfOpenEvent, context, new OnCircuitHalfOpenedArguments()); isHalfOpen = true; } @@ -143,7 +143,7 @@ public ValueTask CloseCircuitAsync(ResilienceContext context) if (isHalfOpen && _onHalfOpen is not null) { - _executor.ScheduleTask(() => _onHalfOpen(new OnCircuitHalfOpenedArguments(context)).AsTask(), context, out task); + _executor.ScheduleTask(() => _onHalfOpen(new OnCircuitHalfOpenedArguments()).AsTask(), context, out task); } } @@ -270,12 +270,12 @@ private void CloseCircuit_NeedsLock(Outcome outcome, bool manu if (priorState != CircuitState.Closed) { - var args = new OnCircuitClosedArguments(context, manual); - _telemetry.Report(CircuitBreakerConstants.OnCircuitClosed, outcome, args); + var args = new OutcomeArguments(context, outcome, new OnCircuitClosedArguments(manual)); + _telemetry.Report(CircuitBreakerConstants.OnCircuitClosed, args); if (_onClosed is not null) { - _executor.ScheduleTask(() => _onClosed.HandleAsync(outcome, args).AsTask(), context, out scheduledTask); + _executor.ScheduleTask(() => _onClosed.HandleAsync(args).AsTask(), context, out scheduledTask); } } } @@ -324,12 +324,12 @@ private void OpenCircuitFor_NeedsLock(Outcome outcome, TimeSpa var transitionedState = _circuitState; _circuitState = CircuitState.Open; - var args = new OnCircuitOpenedArguments(context, breakDuration, manual); - _telemetry.Report(CircuitBreakerConstants.OnCircuitOpened, outcome, args); + var args = new OutcomeArguments(context, outcome, new OnCircuitOpenedArguments(breakDuration, manual)); + _telemetry.Report(CircuitBreakerConstants.OnCircuitOpened, args); if (_onOpened is not null) { - _executor.ScheduleTask(() => _onOpened.HandleAsync(outcome, args).AsTask(), context, out scheduledTask); + _executor.ScheduleTask(() => _onOpened.HandleAsync(args).AsTask(), context, out scheduledTask); } } } diff --git a/src/Polly.Core/CircuitBreaker/OnCircuitClosedArguments.cs b/src/Polly.Core/CircuitBreaker/OnCircuitClosedArguments.cs index 3c9dc232762..54b183143a7 100644 --- a/src/Polly.Core/CircuitBreaker/OnCircuitClosedArguments.cs +++ b/src/Polly.Core/CircuitBreaker/OnCircuitClosedArguments.cs @@ -1,10 +1,7 @@ -using Polly.Strategy; - namespace Polly.CircuitBreaker; /// /// Arguments used by event. /// -/// The context associated with the execution of a user-provided callback. /// Indicates whether the circuit was closed manually by using . -public readonly record struct OnCircuitClosedArguments(ResilienceContext Context, bool IsManual) : IResilienceArguments; +public readonly record struct OnCircuitClosedArguments(bool IsManual); diff --git a/src/Polly.Core/CircuitBreaker/OnCircuitHalfOpenedArguments.cs b/src/Polly.Core/CircuitBreaker/OnCircuitHalfOpenedArguments.cs index e9157a4307e..2d885822fcd 100644 --- a/src/Polly.Core/CircuitBreaker/OnCircuitHalfOpenedArguments.cs +++ b/src/Polly.Core/CircuitBreaker/OnCircuitHalfOpenedArguments.cs @@ -1,9 +1,6 @@ -using Polly.Strategy; - namespace Polly.CircuitBreaker; /// /// Arguments used by event. /// -/// The context associated with the execution of a user-provided callback. -public readonly record struct OnCircuitHalfOpenedArguments(ResilienceContext Context) : IResilienceArguments; +public readonly record struct OnCircuitHalfOpenedArguments(); diff --git a/src/Polly.Core/CircuitBreaker/OnCircuitOpenedArguments.cs b/src/Polly.Core/CircuitBreaker/OnCircuitOpenedArguments.cs index b0b3f953de0..34ba00ed9d2 100644 --- a/src/Polly.Core/CircuitBreaker/OnCircuitOpenedArguments.cs +++ b/src/Polly.Core/CircuitBreaker/OnCircuitOpenedArguments.cs @@ -1,11 +1,8 @@ -using Polly.Strategy; - namespace Polly.CircuitBreaker; /// /// Arguments used by event. /// -/// The context associated with the execution of a user-provided callback. /// The duration of break. /// Indicates whether the circuit was opened manually by using . -public readonly record struct OnCircuitOpenedArguments(ResilienceContext Context, TimeSpan BreakDuration, bool IsManual) : IResilienceArguments; +public readonly record struct OnCircuitOpenedArguments(TimeSpan BreakDuration, bool IsManual); diff --git a/src/Polly.Core/Fallback/FallbackHandler.Handler.cs b/src/Polly.Core/Fallback/FallbackHandler.Handler.cs index 9618a56481e..bcfc6a39460 100644 --- a/src/Polly.Core/Fallback/FallbackHandler.Handler.cs +++ b/src/Polly.Core/Fallback/FallbackHandler.Handler.cs @@ -10,14 +10,14 @@ internal sealed class Handler internal Handler(Dictionary handlers) => _handlers = handlers; - public ValueTask, HandleFallbackArguments, ValueTask>?> ShouldHandleAsync(Outcome outcome, HandleFallbackArguments arguments) + public ValueTask, ValueTask>?> ShouldHandleAsync(OutcomeArguments args) { if (!_handlers.TryGetValue(typeof(TResult), out var handler)) { - return new((Func, HandleFallbackArguments, ValueTask>?)null); + return new((Func, ValueTask>?)null); } - return ((FallbackHandler)handler).ShouldHandleAsync(outcome, arguments); + return ((FallbackHandler)handler).ShouldHandleAsync(args); } } } diff --git a/src/Polly.Core/Fallback/FallbackHandler.TResult.cs b/src/Polly.Core/Fallback/FallbackHandler.TResult.cs index 6187de50ecf..196198692e5 100644 --- a/src/Polly.Core/Fallback/FallbackHandler.TResult.cs +++ b/src/Polly.Core/Fallback/FallbackHandler.TResult.cs @@ -21,7 +21,7 @@ internal sealed class FallbackHandler /// This property is required. Defaults to . /// [Required] - public Func, HandleFallbackArguments, ValueTask>? ShouldHandle { get; set; } + public Func, ValueTask>? ShouldHandle { get; set; } /// /// Gets or sets the fallback action to be executed if predicate evaluates as true. @@ -30,13 +30,12 @@ internal sealed class FallbackHandler /// This property is required. Defaults to . /// [Required] - public Func, HandleFallbackArguments, ValueTask>? FallbackAction { get; set; } = null; + public Func, ValueTask>? FallbackAction { get; set; } = null; - internal async ValueTask, HandleFallbackArguments, ValueTask>?> ShouldHandleAsync( - Outcome outcome, - HandleFallbackArguments arguments) + internal async ValueTask, ValueTask>?> ShouldHandleAsync( + OutcomeArguments args) { - if (!await ShouldHandle!(outcome, arguments).ConfigureAwait(arguments.Context.ContinueOnCapturedContext)) + if (!await ShouldHandle!(args).ConfigureAwait(args.Context.ContinueOnCapturedContext)) { return null; } diff --git a/src/Polly.Core/Fallback/FallbackHandler.cs b/src/Polly.Core/Fallback/FallbackHandler.cs index 319dc0e66e0..4dc73ec028f 100644 --- a/src/Polly.Core/Fallback/FallbackHandler.cs +++ b/src/Polly.Core/Fallback/FallbackHandler.cs @@ -64,12 +64,12 @@ private static FallbackHandler CreateGenericHandler(VoidFallbackHand { return new() { - FallbackAction = async (outcome, args) => + FallbackAction = async args => { - await handler.FallbackAction!(outcome.AsOutcome(), args).ConfigureAwait(args.Context.ContinueOnCapturedContext); + await handler.FallbackAction!(args.AsObjectArguments()).ConfigureAwait(args.Context.ContinueOnCapturedContext); return VoidResult.Instance; }, - ShouldHandle = (outcome, args) => handler.ShouldHandle!(outcome.AsOutcome(), args) + ShouldHandle = args => handler.ShouldHandle!(args.AsObjectArguments()) }; } } diff --git a/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs b/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs index fcd314ef6d8..1a6bf862a51 100644 --- a/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs +++ b/src/Polly.Core/Fallback/FallbackResilienceStrategy.cs @@ -26,24 +26,26 @@ protected internal override async ValueTask> ExecuteCoreAsync(context, outcome, new HandleFallbackArguments()); + var action = await _handler.ShouldHandleAsync(handleFallbackArgs).ConfigureAwait(context.ContinueOnCapturedContext); if (action == null) { return outcome; } - _telemetry.Report(FallbackConstants.OnFallback, outcome, args); + var onFallbackArgs = new OutcomeArguments(context, outcome, new OnFallbackArguments()); + + _telemetry.Report(FallbackConstants.OnFallback, onFallbackArgs); if (_onFallback is not null) { - await _onFallback.HandleAsync(outcome, new OnFallbackArguments(context)).ConfigureAwait(context.ContinueOnCapturedContext); + await _onFallback.HandleAsync(onFallbackArgs).ConfigureAwait(context.ContinueOnCapturedContext); } try { - return new Outcome(await action(outcome, args).ConfigureAwait(context.ContinueOnCapturedContext)); + return new Outcome(await action(handleFallbackArgs).ConfigureAwait(context.ContinueOnCapturedContext)); } catch (Exception e) { diff --git a/src/Polly.Core/Fallback/FallbackResilienceStrategyBuilderExtensions.cs b/src/Polly.Core/Fallback/FallbackResilienceStrategyBuilderExtensions.cs index 38a33b88e8f..2f068eab74d 100644 --- a/src/Polly.Core/Fallback/FallbackResilienceStrategyBuilderExtensions.cs +++ b/src/Polly.Core/Fallback/FallbackResilienceStrategyBuilderExtensions.cs @@ -22,7 +22,7 @@ public static class FallbackResilienceStrategyBuilderExtensions public static ResilienceStrategyBuilder AddFallback( this ResilienceStrategyBuilder builder, Action> shouldHandle, - Func, HandleFallbackArguments, ValueTask> fallbackAction) + Func, ValueTask> fallbackAction) { Guard.NotNull(builder); Guard.NotNull(shouldHandle); diff --git a/src/Polly.Core/Fallback/FallbackStrategyOptions.TResult.cs b/src/Polly.Core/Fallback/FallbackStrategyOptions.TResult.cs index 0d4996d8226..687d2711b29 100644 --- a/src/Polly.Core/Fallback/FallbackStrategyOptions.TResult.cs +++ b/src/Polly.Core/Fallback/FallbackStrategyOptions.TResult.cs @@ -22,7 +22,7 @@ public class FallbackStrategyOptions : ResilienceStrategyOptions /// This property is required. Defaults to . /// [Required] - public Func, HandleFallbackArguments, ValueTask>? ShouldHandle { get; set; } + public Func, ValueTask>? ShouldHandle { get; set; } /// /// Gets or sets the fallback action to be executed when the predicate evaluates as true. @@ -31,7 +31,7 @@ public class FallbackStrategyOptions : ResilienceStrategyOptions /// This property is required. Defaults to . /// [Required] - public Func, HandleFallbackArguments, ValueTask>? FallbackAction { get; set; } + public Func, ValueTask>? FallbackAction { get; set; } /// /// Gets or sets the outcome event instance responsible for triggering fallback events. @@ -39,6 +39,6 @@ public class FallbackStrategyOptions : ResilienceStrategyOptions /// /// Defaults to instance. /// - public Func, OnFallbackArguments, ValueTask>? OnFallback { get; set; } + public Func, ValueTask>? OnFallback { get; set; } } diff --git a/src/Polly.Core/Fallback/FallbackStrategyOptions.cs b/src/Polly.Core/Fallback/FallbackStrategyOptions.cs index 93fd8905405..03d9533e2b3 100644 --- a/src/Polly.Core/Fallback/FallbackStrategyOptions.cs +++ b/src/Polly.Core/Fallback/FallbackStrategyOptions.cs @@ -29,6 +29,6 @@ internal class FallbackStrategyOptions : ResilienceStrategyOptions /// /// Defaults to . /// - public Func, OnFallbackArguments, ValueTask>? OnFallback { get; set; } + public Func, ValueTask>? OnFallback { get; set; } } diff --git a/src/Polly.Core/Fallback/HandleFallbackArguments.cs b/src/Polly.Core/Fallback/HandleFallbackArguments.cs index 3734c87161f..8ec064c863f 100644 --- a/src/Polly.Core/Fallback/HandleFallbackArguments.cs +++ b/src/Polly.Core/Fallback/HandleFallbackArguments.cs @@ -1,9 +1,6 @@ -using Polly.Strategy; - namespace Polly.Fallback; /// /// Represents arguments used in fallback handling scenarios. /// -/// The context associated with the execution of a user-provided callback. -public readonly record struct HandleFallbackArguments(ResilienceContext Context) : IResilienceArguments; +public readonly record struct HandleFallbackArguments(); diff --git a/src/Polly.Core/Fallback/OnFallbackArguments.cs b/src/Polly.Core/Fallback/OnFallbackArguments.cs index 3be4c673e8a..4e903e3cacc 100644 --- a/src/Polly.Core/Fallback/OnFallbackArguments.cs +++ b/src/Polly.Core/Fallback/OnFallbackArguments.cs @@ -1,9 +1,6 @@ -using Polly.Strategy; - namespace Polly.Fallback; /// /// Represents arguments used in fallback handling scenarios. /// -/// The context associated with the execution of a user-provided callback. -public readonly record struct OnFallbackArguments(ResilienceContext Context) : IResilienceArguments; +public readonly record struct OnFallbackArguments(); diff --git a/src/Polly.Core/Fallback/VoidFallbackHandler.cs b/src/Polly.Core/Fallback/VoidFallbackHandler.cs index 14343004f63..dde50057293 100644 --- a/src/Polly.Core/Fallback/VoidFallbackHandler.cs +++ b/src/Polly.Core/Fallback/VoidFallbackHandler.cs @@ -19,7 +19,7 @@ internal sealed class VoidFallbackHandler /// This property is required. Defaults to . /// [Required] - public Func, HandleFallbackArguments, ValueTask>? ShouldHandle { get; set; } + public Func, ValueTask>? ShouldHandle { get; set; } /// /// Gets or sets the fallback action to be executed when the predicate evaluates as true. @@ -28,5 +28,5 @@ internal sealed class VoidFallbackHandler /// This property is required. Defaults to . /// [Required] - public Func, HandleFallbackArguments, ValueTask>? FallbackAction { get; set; } = null; + public Func, ValueTask>? FallbackAction { get; set; } = null; } diff --git a/src/Polly.Core/Hedging/Controller/TaskExecution.cs b/src/Polly.Core/Hedging/Controller/TaskExecution.cs index 2c0b89bf106..4b01c5ae7e1 100644 --- a/src/Polly.Core/Hedging/Controller/TaskExecution.cs +++ b/src/Polly.Core/Hedging/Controller/TaskExecution.cs @@ -195,8 +195,9 @@ private async Task ExecutePrimaryActionAsync(Func(Outcome outcome) { + var args = new OutcomeArguments(Context, outcome, new HandleHedgingArguments()); Outcome = outcome.AsOutcome(); - IsHandled = await _handler.ShouldHandleAsync(outcome, new HandleHedgingArguments(Context)).ConfigureAwait(Context.ContinueOnCapturedContext); + IsHandled = await _handler.ShouldHandleAsync(args).ConfigureAwait(Context.ContinueOnCapturedContext); } private void PrepareContext(ref ContextSnapshot snapshot) diff --git a/src/Polly.Core/Hedging/HandleHedgingArguments.cs b/src/Polly.Core/Hedging/HandleHedgingArguments.cs index 75869e23216..dd5c6dc37e4 100644 --- a/src/Polly.Core/Hedging/HandleHedgingArguments.cs +++ b/src/Polly.Core/Hedging/HandleHedgingArguments.cs @@ -1,9 +1,6 @@ -using Polly.Strategy; - namespace Polly.Hedging; /// /// Represents arguments used in hedging handling scenarios. /// -/// The context associated with the execution of a user-provided callback. -public readonly record struct HandleHedgingArguments(ResilienceContext Context) : IResilienceArguments; +public readonly record struct HandleHedgingArguments(); diff --git a/src/Polly.Core/Hedging/HedgingActionGeneratorArguments.TResult.cs b/src/Polly.Core/Hedging/HedgingActionGeneratorArguments.TResult.cs index c67acc66437..3ed8708ff84 100644 --- a/src/Polly.Core/Hedging/HedgingActionGeneratorArguments.TResult.cs +++ b/src/Polly.Core/Hedging/HedgingActionGeneratorArguments.TResult.cs @@ -1,5 +1,3 @@ -using Polly.Strategy; - namespace Polly.Hedging; /// @@ -8,4 +6,4 @@ namespace Polly.Hedging; /// The type of the result. /// The context associated with the execution of a user-provided callback. /// The zero-based hedging attempt number. -public readonly record struct HedgingActionGeneratorArguments(ResilienceContext Context, int Attempt) : IResilienceArguments; +public readonly record struct HedgingActionGeneratorArguments(ResilienceContext Context, int Attempt); diff --git a/src/Polly.Core/Hedging/HedgingActionGeneratorArguments.cs b/src/Polly.Core/Hedging/HedgingActionGeneratorArguments.cs index 089d54ffaaa..5aac7ac69d1 100644 --- a/src/Polly.Core/Hedging/HedgingActionGeneratorArguments.cs +++ b/src/Polly.Core/Hedging/HedgingActionGeneratorArguments.cs @@ -1,5 +1,3 @@ -using Polly.Strategy; - namespace Polly.Hedging; /// @@ -7,4 +5,4 @@ namespace Polly.Hedging; /// /// The context associated with the execution of a user-provided callback. /// The zero-based hedging attempt number. -internal readonly record struct HedgingActionGeneratorArguments(ResilienceContext Context, int Attempt) : IResilienceArguments; +internal readonly record struct HedgingActionGeneratorArguments(ResilienceContext Context, int Attempt); diff --git a/src/Polly.Core/Hedging/HedgingDelayArguments.cs b/src/Polly.Core/Hedging/HedgingDelayArguments.cs index 3c333eac64b..153fface0b1 100644 --- a/src/Polly.Core/Hedging/HedgingDelayArguments.cs +++ b/src/Polly.Core/Hedging/HedgingDelayArguments.cs @@ -1,5 +1,3 @@ -using Polly.Strategy; - namespace Polly.Hedging; /// @@ -7,4 +5,4 @@ namespace Polly.Hedging; /// /// The context associated with the execution of a user-provided callback. /// The zero-based hedging attempt number. -public readonly record struct HedgingDelayArguments(ResilienceContext Context, int Attempt) : IResilienceArguments; +public readonly record struct HedgingDelayArguments(ResilienceContext Context, int Attempt); diff --git a/src/Polly.Core/Hedging/HedgingHandler.Handler.cs b/src/Polly.Core/Hedging/HedgingHandler.Handler.cs index f268cd1dda7..31c4dc190c7 100644 --- a/src/Polly.Core/Hedging/HedgingHandler.Handler.cs +++ b/src/Polly.Core/Hedging/HedgingHandler.Handler.cs @@ -17,7 +17,7 @@ internal Handler(Dictionary predicates, Dictionary g public bool HandlesHedging() => _generators.ContainsKey(typeof(TResult)); - public ValueTask ShouldHandleAsync(Outcome outcome, HandleHedgingArguments arguments) + public ValueTask ShouldHandleAsync(OutcomeArguments args) { if (!_predicates.TryGetValue(typeof(TResult), out var predicate)) { @@ -26,11 +26,11 @@ public ValueTask ShouldHandleAsync(Outcome outcome, Hand if (typeof(TResult) == typeof(VoidResult)) { - return ((Func, HandleHedgingArguments, ValueTask>)predicate)(outcome.AsOutcome(), arguments); + return ((Func, ValueTask>)predicate)(args.AsObjectArguments()); } else { - return ((Func, HandleHedgingArguments, ValueTask>)predicate)(outcome, arguments); + return ((Func, ValueTask>)predicate)(args); } } diff --git a/src/Polly.Core/Hedging/HedgingHandler.TResult.cs b/src/Polly.Core/Hedging/HedgingHandler.TResult.cs index 8ef6d86b724..44253a46c9a 100644 --- a/src/Polly.Core/Hedging/HedgingHandler.TResult.cs +++ b/src/Polly.Core/Hedging/HedgingHandler.TResult.cs @@ -20,7 +20,7 @@ internal sealed class HedgingHandler /// This property is required. Defaults to . /// [Required] - public Func, HandleHedgingArguments, ValueTask>? ShouldHandle { get; set; } + public Func, ValueTask>? ShouldHandle { get; set; } /// /// Gets or sets the hedging action generator that creates hedged actions. diff --git a/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs b/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs index 4585904901d..4596597212d 100644 --- a/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs +++ b/src/Polly.Core/Hedging/HedgingResilienceStrategy.cs @@ -86,15 +86,15 @@ protected internal override async ValueTask> ExecuteCoreAsync(context, outcome, new OnHedgingArguments(hedgingContext.LoadedTasks - 1)); + _telemetry.Report(HedgingConstants.OnHedgingEventName, onHedgingArgs); if (OnHedging is not null) { // If nothing has been returned or thrown yet, the result is a transient failure, // and other hedged request will be awaited. // Before it, one needs to perform the task adjacent to each hedged call. - await OnHedging.HandleAsync(outcome, onHedgingArgs).ConfigureAwait(continueOnCapturedContext); + await OnHedging.HandleAsync(onHedgingArgs).ConfigureAwait(continueOnCapturedContext); } } } diff --git a/src/Polly.Core/Hedging/HedgingStrategyOptions.TResult.cs b/src/Polly.Core/Hedging/HedgingStrategyOptions.TResult.cs index fc0244b6428..31f6e016937 100644 --- a/src/Polly.Core/Hedging/HedgingStrategyOptions.TResult.cs +++ b/src/Polly.Core/Hedging/HedgingStrategyOptions.TResult.cs @@ -49,7 +49,7 @@ public class HedgingStrategyOptions : ResilienceStrategyOptions /// This property is required. Defaults to . /// [Required] - public Func, HandleHedgingArguments, ValueTask>? ShouldHandle { get; set; } + public Func, ValueTask>? ShouldHandle { get; set; } /// /// Gets or sets the hedging action generator that creates hedged actions. @@ -75,5 +75,5 @@ public class HedgingStrategyOptions : ResilienceStrategyOptions /// /// Defaults to . /// - public Func, OnHedgingArguments, ValueTask>? OnHedging { get; set; } + public Func, ValueTask>? OnHedging { get; set; } } diff --git a/src/Polly.Core/Hedging/HedgingStrategyOptions.cs b/src/Polly.Core/Hedging/HedgingStrategyOptions.cs index 29415b5b6e1..d7d517b82cd 100644 --- a/src/Polly.Core/Hedging/HedgingStrategyOptions.cs +++ b/src/Polly.Core/Hedging/HedgingStrategyOptions.cs @@ -64,5 +64,5 @@ internal class HedgingStrategyOptions : ResilienceStrategyOptions /// /// Defaults to . /// - public Func, OnHedgingArguments, ValueTask>? OnHedging { get; set; } + public Func, ValueTask>? OnHedging { get; set; } } diff --git a/src/Polly.Core/Hedging/OnHedgingArguments.cs b/src/Polly.Core/Hedging/OnHedgingArguments.cs index 97c8c120257..53dc32babef 100644 --- a/src/Polly.Core/Hedging/OnHedgingArguments.cs +++ b/src/Polly.Core/Hedging/OnHedgingArguments.cs @@ -1,10 +1,7 @@ -using Polly.Strategy; - namespace Polly.Hedging; /// /// Represents arguments used by the on-hedging event. /// -/// The context associated with the execution of a user-provided callback. /// The zero-based hedging attempt number. -public readonly record struct OnHedgingArguments(ResilienceContext Context, int Attempt) : IResilienceArguments; +public readonly record struct OnHedgingArguments(int Attempt); diff --git a/src/Polly.Core/Hedging/VoidHedgingHandler.cs b/src/Polly.Core/Hedging/VoidHedgingHandler.cs index 25b93056c7d..ce352061853 100644 --- a/src/Polly.Core/Hedging/VoidHedgingHandler.cs +++ b/src/Polly.Core/Hedging/VoidHedgingHandler.cs @@ -19,7 +19,7 @@ internal sealed class VoidHedgingHandler /// This property is required. Defaults to . /// [Required] - public Func, HandleHedgingArguments, ValueTask>? ShouldHandle { get; set; } + public Func, ValueTask>? ShouldHandle { get; set; } /// /// Gets or sets the hedging action generator that creates hedged actions. diff --git a/src/Polly.Core/README.md b/src/Polly.Core/README.md index 739b54733a5..73df356b3c5 100644 --- a/src/Polly.Core/README.md +++ b/src/Polly.Core/README.md @@ -178,26 +178,26 @@ Resilience strategies leverage the following delegate types: The suggested signatures for these delegates are as follows: **Predicates** -- `Func, TArgs, ValueTask>`: This is the predicate for the generic outcome. -- `Func>`: This is the predicate for the non-generic outcome. +- `Func, ValueTask>`: This is the predicate for the generic outcome. +- `Func, ValueTask>`: This is the predicate for the non-generic outcome. **Events** -- `Func, TArgs, ValueTask>`: This is the event for the generic outcome. -- `Func`: This is the event for the non-generic outcome. +- `Func, ValueTask>`: This is the event for the generic outcome. +- `Func, ValueTask>`: This is the event for the non-generic outcome. - `Func`: This is the event utilized by strategies that do not operate with an outcome (for example, Timeout, RateLimiter). **Generators** -- `Func, TArgs, ValueTask>`: This is the generator for the generic outcome. -- `Func>`: This is the generator for the non-generic outcome. +- `Func, ValueTask>`: This is the generator for the generic outcome. +- `Func, ValueTask>`: This is the generator for the non-generic outcome. - `Func>`: This is the generator used by strategies that do not operate with an outcome (for example, Timeout, RateLimiter). -It's essential to note that all these delegates are asynchronous and return a `ValueTask`. +It's essential to note that all these delegates are asynchronous and return a `ValueTask`. -The delegates employ the following components: +The **`OutcomeArguments`** captures the following information that can be used by the delegate: -- **`IResilienceArguments`**: This interface sets out the preferred structure for arguments employed by individual strategies. It features a single property, `Context`, which supplies the context linked with the execution of a user-supplied callback. -- **`Outcome`**: This captures the result of an operation that yields a result of a specific type, `TResult`, or an exception. This structure specializes its functionality to accommodate generic results. The `TryGetResult` method, for instance, is customized to manage the generic result type, offering additional flexibility and extensibility. -- **`Outcome`**: This represents a non-generic outcome of an operation, encompassing both a result and an exception, if one occurred. This structure is equipped with numerous properties and methods, such as `HasResult`, `IsVoidResult`, and `TryGetResult`, which facilitate the management and evaluation of the outcome of an operation. +- `Outcome`: This captures the result of an operation that yields a result of a specific type, `TResult`, or an exception. +- `Context`: The `ResilienceContext` of the operation. +- `Arguments`: Additional arguments associated with the operation. Each resilience strategy can define different arguments for different operations or events. ## Examples @@ -209,7 +209,7 @@ A non-generic predicate defining retries for multiple result types: new ResilienceStrategyBuilder() .AddRetry(new RetryStrategyOptions { - ShouldRetry = (outcome, _) => outcome switch + ShouldRetry = args => args switch { { Exception: InvalidOperationException } => PredicateResult.True, { Result: string result } when result == Failure => PredicateResult.True, @@ -226,7 +226,7 @@ A generic predicate defining retries for a single result type: new ResilienceStrategyBuilder() .AddRetry(new RetryStrategyOptions { - ShouldRetry = (outcome, _) => outcome switch + ShouldRetry = args => args switch { { Exception: InvalidOperationException } => PredicateResult.True, { Result: result } when result == Failure => PredicateResult.True, diff --git a/src/Polly.Core/Retry/OnRetryArguments.cs b/src/Polly.Core/Retry/OnRetryArguments.cs index 0db828ce81b..7160b92ab9c 100644 --- a/src/Polly.Core/Retry/OnRetryArguments.cs +++ b/src/Polly.Core/Retry/OnRetryArguments.cs @@ -1,11 +1,8 @@ -using Polly.Strategy; - namespace Polly.Retry; /// /// Represents the arguments used by for handling the retry event. /// -/// The context associated with the execution of a user-provided callback. /// The zero-based attempt number. The first attempt is 0, the second attempt is 1, and so on. /// The delay before the next retry. -public readonly record struct OnRetryArguments(ResilienceContext Context, int Attempt, TimeSpan RetryDelay) : IResilienceArguments; +public readonly record struct OnRetryArguments(int Attempt, TimeSpan RetryDelay); diff --git a/src/Polly.Core/Retry/RetryDelayArguments.cs b/src/Polly.Core/Retry/RetryDelayArguments.cs index 907a23721f9..ae2b3871159 100644 --- a/src/Polly.Core/Retry/RetryDelayArguments.cs +++ b/src/Polly.Core/Retry/RetryDelayArguments.cs @@ -1,11 +1,8 @@ -using Polly.Strategy; - namespace Polly.Retry; /// /// Represents the arguments used by for generating the next retry delay. /// -/// The context associated with the execution of a user-provided callback. /// The zero-based attempt number. The first attempt is 0, the second attempt is 1, and so on. /// The delay suggested by the retry strategy. -public readonly record struct RetryDelayArguments(ResilienceContext Context, int Attempt, TimeSpan DelayHint) : IResilienceArguments; +public readonly record struct RetryDelayArguments(int Attempt, TimeSpan DelayHint); diff --git a/src/Polly.Core/Retry/RetryResilienceStrategy.cs b/src/Polly.Core/Retry/RetryResilienceStrategy.cs index 23d56493689..dafab1b1129 100644 --- a/src/Polly.Core/Retry/RetryResilienceStrategy.cs +++ b/src/Polly.Core/Retry/RetryResilienceStrategy.cs @@ -60,8 +60,10 @@ protected internal override async ValueTask> ExecuteCoreAsync outcome = await callback(context, state).ConfigureAwait(context.ContinueOnCapturedContext); - if (IsLastAttempt(attempt) || !await ShouldRetry.HandleAsync(outcome, new ShouldRetryArguments(context, attempt)).ConfigureAwait(context.ContinueOnCapturedContext)) + var outcome = await callback(context, state).ConfigureAwait(context.ContinueOnCapturedContext); + var shouldRetryArgs = new OutcomeArguments(context, outcome, new ShouldRetryArguments(attempt)); + + if (IsLastAttempt(attempt) || !await ShouldRetry.HandleAsync(shouldRetryArgs).ConfigureAwait(context.ContinueOnCapturedContext)) { return outcome; } @@ -69,20 +71,20 @@ protected internal override async ValueTask> ExecuteCoreAsync(context, outcome, new RetryDelayArguments(attempt, delay)); + var newDelay = await DelayGenerator.HandleAsync(delayArgs).ConfigureAwait(false); if (RetryHelper.IsValidDelay(newDelay)) { delay = newDelay; } } - var args = new OnRetryArguments(context, attempt, delay); - - _telemetry.Report(RetryConstants.OnRetryEvent, outcome, args); + var onRetryArgs = new OutcomeArguments(context, outcome, new OnRetryArguments(attempt, delay)); + _telemetry.Report(RetryConstants.OnRetryEvent, onRetryArgs); if (OnRetry is not null) { - await OnRetry.HandleAsync(outcome, args).ConfigureAwait(context.ContinueOnCapturedContext); + await OnRetry.HandleAsync(onRetryArgs).ConfigureAwait(context.ContinueOnCapturedContext); } if (outcome.TryGetResult(out var resultValue)) diff --git a/src/Polly.Core/Retry/RetryResilienceStrategyBuilderExtensions.cs b/src/Polly.Core/Retry/RetryResilienceStrategyBuilderExtensions.cs index b39ea216ec7..2615353fd57 100644 --- a/src/Polly.Core/Retry/RetryResilienceStrategyBuilderExtensions.cs +++ b/src/Polly.Core/Retry/RetryResilienceStrategyBuilderExtensions.cs @@ -53,7 +53,7 @@ public static ResilienceStrategyBuilder AddRetry( var options = new RetryStrategyOptions(); ConfigureShouldRetry(shouldRetry, options); - options.RetryDelayGenerator = (_, args) => new ValueTask(retryDelayGenerator(args.Attempt)); + options.RetryDelayGenerator = args => new ValueTask(retryDelayGenerator(args.Arguments.Attempt)); return builder.AddRetry(options); } diff --git a/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs b/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs index 38f5cdf04f9..8cd7429dce5 100644 --- a/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs +++ b/src/Polly.Core/Retry/RetryStrategyOptions.TResult.cs @@ -65,7 +65,7 @@ public class RetryStrategyOptions : ResilienceStrategyOptions /// Defaults to . This property is required. /// [Required] - public Func, ShouldRetryArguments, ValueTask>? ShouldRetry { get; set; } + public Func, ValueTask>? ShouldRetry { get; set; } /// /// Gets or sets the generator instance that is used to calculate the time between retries. @@ -73,7 +73,7 @@ public class RetryStrategyOptions : ResilienceStrategyOptions /// /// Defaults to . /// - public Func, RetryDelayArguments, ValueTask>? RetryDelayGenerator { get; set; } + public Func, ValueTask>? RetryDelayGenerator { get; set; } /// /// Gets or sets an outcome event that is used to register on-retry callbacks. @@ -89,5 +89,5 @@ public class RetryStrategyOptions : ResilienceStrategyOptions /// Defaults to . /// /// - public Func, OnRetryArguments, ValueTask>? OnRetry { get; set; } + public Func, ValueTask>? OnRetry { get; set; } } diff --git a/src/Polly.Core/Retry/ShouldRetryArguments.cs b/src/Polly.Core/Retry/ShouldRetryArguments.cs index 10aefccafe8..7899113ba16 100644 --- a/src/Polly.Core/Retry/ShouldRetryArguments.cs +++ b/src/Polly.Core/Retry/ShouldRetryArguments.cs @@ -1,10 +1,7 @@ -using Polly.Strategy; - namespace Polly.Retry; /// /// Represents the arguments used by for determining whether a retry should be performed. /// -/// The context associated with the execution of a user-provided callback. /// The zero-based attempt number. The first attempt is 0, the second attempt is 1, and so on. -public readonly record struct ShouldRetryArguments(ResilienceContext Context, int Attempt) : IResilienceArguments; +public readonly record struct ShouldRetryArguments(int Attempt); diff --git a/src/Polly.Core/Strategy/EventInvoker.cs b/src/Polly.Core/Strategy/EventInvoker.cs index a2da6673da5..3b6d7c1d864 100644 --- a/src/Polly.Core/Strategy/EventInvoker.cs +++ b/src/Polly.Core/Strategy/EventInvoker.cs @@ -3,37 +3,36 @@ namespace Polly.Strategy; internal abstract class EventInvoker - where TArgs : IResilienceArguments { - public static EventInvoker? Create(Func, TArgs, ValueTask>? callback, bool isGeneric) => callback switch + public static EventInvoker? Create(Func, ValueTask>? callback, bool isGeneric) => callback switch { - Func, TArgs, ValueTask> generic when !isGeneric => new NonGenericEventInvoker(generic), + Func, ValueTask> generic when !isGeneric => new NonGenericEventInvoker(generic), { } => new GenericEventInvoker(callback), _ => null, }; - public abstract ValueTask HandleAsync(Outcome outcome, TArgs args); + public abstract ValueTask HandleAsync(OutcomeArguments args); private sealed class NonGenericEventInvoker : EventInvoker { - private readonly Func, TArgs, ValueTask> _callback; + private readonly Func, ValueTask> _callback; - public NonGenericEventInvoker(Func, TArgs, ValueTask> callback) => _callback = callback; + public NonGenericEventInvoker(Func, ValueTask> callback) => _callback = callback; - public override ValueTask HandleAsync(Outcome outcome, TArgs args) => _callback(outcome.AsOutcome(), args); + public override ValueTask HandleAsync(OutcomeArguments args) => _callback(args.AsObjectArguments()); } private sealed class GenericEventInvoker : EventInvoker { private readonly object _callback; - public GenericEventInvoker(Func, TArgs, ValueTask> callback) => _callback = callback; + public GenericEventInvoker(Func, ValueTask> callback) => _callback = callback; - public override ValueTask HandleAsync(Outcome outcome, TArgs args) + public override ValueTask HandleAsync(OutcomeArguments args) { if (typeof(TResult) == typeof(T)) { - return ((Func, TArgs, ValueTask>)_callback)(outcome, args); + return ((Func, ValueTask>)_callback)(args); } return default; diff --git a/src/Polly.Core/Strategy/GeneratorInvoker.cs b/src/Polly.Core/Strategy/GeneratorInvoker.cs index 8a3aa4d9f47..a06d85bb688 100644 --- a/src/Polly.Core/Strategy/GeneratorInvoker.cs +++ b/src/Polly.Core/Strategy/GeneratorInvoker.cs @@ -3,27 +3,26 @@ namespace Polly.Strategy; internal abstract class GeneratorInvoker - where TArgs : IResilienceArguments { public static GeneratorInvoker? Create( - Func, TArgs, ValueTask>? generator, + Func, ValueTask>? generator, TValue defaultValue, bool isGeneric) => generator switch { - Func, TArgs, ValueTask> objectGenerator when !isGeneric => new NonGenericGeneratorInvoker(objectGenerator), + Func, ValueTask> objectGenerator when !isGeneric => new NonGenericGeneratorInvoker(objectGenerator), { } => new GenericGeneratorInvoker(generator, defaultValue), _ => null }; - public abstract ValueTask HandleAsync(Outcome outcome, TArgs args); + public abstract ValueTask HandleAsync(OutcomeArguments args); private sealed class NonGenericGeneratorInvoker : GeneratorInvoker { - private readonly Func, TArgs, ValueTask> _generator; + private readonly Func, ValueTask> _generator; - public NonGenericGeneratorInvoker(Func, TArgs, ValueTask> generator) => _generator = generator; + public NonGenericGeneratorInvoker(Func, ValueTask> generator) => _generator = generator; - public override ValueTask HandleAsync(Outcome outcome, TArgs args) => _generator(outcome.AsOutcome(), args); + public override ValueTask HandleAsync(OutcomeArguments args) => _generator(args.AsObjectArguments()); } private sealed class GenericGeneratorInvoker : GeneratorInvoker @@ -31,17 +30,17 @@ private sealed class GenericGeneratorInvoker : GeneratorInvoker, TArgs, ValueTask> generator, TValue defaultValue) + public GenericGeneratorInvoker(Func, ValueTask> generator, TValue defaultValue) { _generator = generator; _defaultValue = defaultValue; } - public override ValueTask HandleAsync(Outcome outcome, TArgs args) + public override ValueTask HandleAsync(OutcomeArguments args) { if (typeof(TResult) == typeof(T)) { - return ((Func, TArgs, ValueTask>)_generator)(outcome, args); + return ((Func, ValueTask>)_generator)(args); } return new(_defaultValue); diff --git a/src/Polly.Core/Strategy/IResilienceArguments.cs b/src/Polly.Core/Strategy/IResilienceArguments.cs deleted file mode 100644 index 65a15d4a68e..00000000000 --- a/src/Polly.Core/Strategy/IResilienceArguments.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Polly.Strategy; - -/// -/// The recommended shape for arguments used by individual strategies. -/// -public interface IResilienceArguments -{ - /// - /// Gets the context associated with the execution of a user-provided callback. - /// - ResilienceContext Context { get; } -} diff --git a/src/Polly.Core/Strategy/OutcomeArguments.cs b/src/Polly.Core/Strategy/OutcomeArguments.cs new file mode 100644 index 00000000000..915fa47c672 --- /dev/null +++ b/src/Polly.Core/Strategy/OutcomeArguments.cs @@ -0,0 +1,56 @@ +#pragma warning disable CA1815 // Override equals and operator equals on value types + +namespace Polly.Strategy +{ + /// + /// Encapsulates the outcome of a resilience operation or event and its associated arguments. + /// + /// The type of the result returned by the resilience operation or event. + /// The type of the additional arguments associated with the specific resilience operation or event. + public readonly struct OutcomeArguments +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + /// + /// Initializes a new instance of the struct. + /// Creates a new instance of . + /// + /// The context in which the resilience operation or event is occurring. + /// The outcome of the resilience operation or event. + /// Additional arguments specific to the resilience operation or event. + public OutcomeArguments(ResilienceContext context, Outcome outcome, TArgs arguments) + { + Guard.NotNull(context); + + Context = context; + Outcome = outcome; + Arguments = arguments; + } + + /// + /// Gets the outcome of the resilience operation or event. + /// + public Outcome Outcome { get; } + + /// + /// Gets the context in which the resilience operation or event occurred. + /// + public ResilienceContext Context { get; } + + /// + /// Gets additional arguments specific to the resilience operation or event. + /// + public TArgs Arguments { get; } + + /// + /// Gets the exception, if any, thrown during the resilience operation or event. + /// + public Exception? Exception => Outcome.Exception; + + /// + /// Gets the result, if any, produced by the resilience operation or event. + /// + public TResult? Result => Outcome.Result; + + internal OutcomeArguments AsObjectArguments() => new(Context, Outcome.AsOutcome(), Arguments); + } +} diff --git a/src/Polly.Core/Strategy/PredicateBuilder.cs b/src/Polly.Core/Strategy/PredicateBuilder.cs index 8aecd9de93f..e41934ba363 100644 --- a/src/Polly.Core/Strategy/PredicateBuilder.cs +++ b/src/Polly.Core/Strategy/PredicateBuilder.cs @@ -89,23 +89,23 @@ public PredicateBuilder HandleResult(TResult result, IEqualityComparer< return HandleResult(r => comparer.Equals(r, result)); } - internal Func, TArgs, ValueTask> CreatePredicate() => _predicates.Count switch + internal Func, ValueTask> CreatePredicate() => _predicates.Count switch { 0 => throw new ValidationException("No predicates were configured. There must be at least one predicate added."), 1 => CreatePredicate(_predicates[0]), _ => CreatePredicate(_predicates.ToArray()), }; - private static Func, TArgs, ValueTask> CreatePredicate(Predicate> predicate) - => (outcome, _) => new ValueTask(predicate(outcome)); + private static Func, ValueTask> CreatePredicate(Predicate> predicate) + => args => new ValueTask(predicate(args.Outcome)); - private static Func, TArgs, ValueTask> CreatePredicate(Predicate>[] predicates) + private static Func, ValueTask> CreatePredicate(Predicate>[] predicates) { - return (outcome, _) => + return args => { foreach (var predicate in predicates) { - if (predicate(outcome)) + if (predicate(args.Outcome)) { return new ValueTask(true); } diff --git a/src/Polly.Core/Strategy/PredicateInvoker.cs b/src/Polly.Core/Strategy/PredicateInvoker.cs index 96be569e6ec..622145719e7 100644 --- a/src/Polly.Core/Strategy/PredicateInvoker.cs +++ b/src/Polly.Core/Strategy/PredicateInvoker.cs @@ -3,37 +3,36 @@ namespace Polly.Strategy; internal abstract class PredicateInvoker - where TArgs : IResilienceArguments { - public static PredicateInvoker? Create(Func, TArgs, ValueTask>? predicate, bool isGeneric) => predicate switch + public static PredicateInvoker? Create(Func, ValueTask>? predicate, bool isGeneric) => predicate switch { - Func, TArgs, ValueTask> objectPredicate when !isGeneric => new NonGenericPredicateInvoker(objectPredicate), + Func, ValueTask> objectPredicate when !isGeneric => new NonGenericPredicateInvoker(objectPredicate), { } => new GenericPredicateInvoker(predicate), _ => null, }; - public abstract ValueTask HandleAsync(Outcome outcome, TArgs args); + public abstract ValueTask HandleAsync(OutcomeArguments args); private sealed class NonGenericPredicateInvoker : PredicateInvoker { - private readonly Func, TArgs, ValueTask> _predicate; + private readonly Func, ValueTask> _predicate; - public NonGenericPredicateInvoker(Func, TArgs, ValueTask> predicate) => _predicate = predicate; + public NonGenericPredicateInvoker(Func, ValueTask> predicate) => _predicate = predicate; - public override ValueTask HandleAsync(Outcome outcome, TArgs args) => _predicate(outcome.AsOutcome(), args); + public override ValueTask HandleAsync(OutcomeArguments args) => _predicate(args.AsObjectArguments()); } private sealed class GenericPredicateInvoker : PredicateInvoker { private readonly object _predicate; - public GenericPredicateInvoker(Func, TArgs, ValueTask> predicate) => _predicate = predicate; + public GenericPredicateInvoker(Func, ValueTask> predicate) => _predicate = predicate; - public override ValueTask HandleAsync(Outcome outcome, TArgs args) + public override ValueTask HandleAsync(OutcomeArguments args) { if (typeof(TResult) == typeof(T)) { - return ((Func, TArgs, ValueTask>)_predicate)(outcome, args); + return ((Func, ValueTask>)_predicate)(args); } return PredicateResult.False; diff --git a/src/Polly.Core/Strategy/ResilienceStrategyBuilderContext.cs b/src/Polly.Core/Strategy/ResilienceStrategyBuilderContext.cs index 3bee9262e2f..40feecc7b94 100644 --- a/src/Polly.Core/Strategy/ResilienceStrategyBuilderContext.cs +++ b/src/Polly.Core/Strategy/ResilienceStrategyBuilderContext.cs @@ -57,22 +57,19 @@ internal ResilienceStrategyBuilderContext( internal bool IsGenericBuilder { get; } - internal PredicateInvoker? CreateInvoker(Func, TArgs, ValueTask>? predicate) - where TArgs : IResilienceArguments + internal PredicateInvoker? CreateInvoker(Func, ValueTask>? predicate) { return PredicateInvoker.Create(predicate, IsGenericBuilder); } - internal EventInvoker? CreateInvoker(Func, TArgs, ValueTask>? callback) - where TArgs : IResilienceArguments + internal EventInvoker? CreateInvoker(Func, ValueTask>? callback) { return EventInvoker.Create(callback, IsGenericBuilder); } internal GeneratorInvoker? CreateInvoker( - Func, TArgs, ValueTask>? generator, + Func, ValueTask>? generator, TValue defaultValue) - where TArgs : IResilienceArguments { return GeneratorInvoker.Create(generator, defaultValue, IsGenericBuilder); } diff --git a/src/Polly.Core/Strategy/ResilienceStrategyTelemetry.cs b/src/Polly.Core/Strategy/ResilienceStrategyTelemetry.cs index 63238ebcf1f..2acd3c02ab5 100644 --- a/src/Polly.Core/Strategy/ResilienceStrategyTelemetry.cs +++ b/src/Polly.Core/Strategy/ResilienceStrategyTelemetry.cs @@ -25,21 +25,22 @@ internal ResilienceStrategyTelemetry(ResilienceTelemetrySource source, Diagnosti /// /// The arguments associated with this event. /// The event name. + /// The resilience context associated with this event. /// The event arguments. /// Thrown when is . - public void Report(string eventName, TArgs args) - where TArgs : IResilienceArguments + public void Report(string eventName, ResilienceContext context, TArgs args) { Guard.NotNull(eventName); + Guard.NotNull(context); - args.Context.AddResilienceEvent(new ReportedResilienceEvent(eventName)); + context.AddResilienceEvent(new ReportedResilienceEvent(eventName)); if (DiagnosticSource is null || !DiagnosticSource.IsEnabled(eventName)) { return; } - DiagnosticSource.Write(eventName, new TelemetryEventArguments(TelemetrySource, eventName, args, null)); + DiagnosticSource.Write(eventName, new TelemetryEventArguments(TelemetrySource, eventName, context, null, args!)); } /// @@ -48,11 +49,9 @@ public void Report(string eventName, TArgs args) /// The arguments associated with this event. /// The type of the result. /// The event name. - /// The outcome associated with the event. /// The event arguments. /// Thrown when is . - public void Report(string eventName, Outcome outcome, TArgs args) - where TArgs : IResilienceArguments + public void Report(string eventName, OutcomeArguments args) { Guard.NotNull(eventName); @@ -63,7 +62,7 @@ public void Report(string eventName, Outcome outcome, T return; } - DiagnosticSource.Write(eventName, new TelemetryEventArguments(TelemetrySource, eventName, args, outcome.AsOutcome())); + DiagnosticSource.Write(eventName, new TelemetryEventArguments(TelemetrySource, eventName, args.Context, args.Outcome.AsOutcome(), args.Arguments!)); } } diff --git a/src/Polly.Core/Telemetry/TelemetryEventArguments.cs b/src/Polly.Core/Telemetry/TelemetryEventArguments.cs index 75dd012b650..82a6b0b858c 100644 --- a/src/Polly.Core/Telemetry/TelemetryEventArguments.cs +++ b/src/Polly.Core/Telemetry/TelemetryEventArguments.cs @@ -2,7 +2,11 @@ namespace Polly.Telemetry; -internal sealed record class TelemetryEventArguments(ResilienceTelemetrySource Source, string EventName, IResilienceArguments Arguments, Outcome? Outcome) : IResilienceArguments +internal sealed record class TelemetryEventArguments( + ResilienceTelemetrySource Source, + string EventName, + ResilienceContext Context, + Outcome? Outcome, + object Arguments) { - public ResilienceContext Context => Arguments.Context; } diff --git a/src/Polly.Core/Timeout/OnTimeoutArguments.cs b/src/Polly.Core/Timeout/OnTimeoutArguments.cs index 99e6053a847..0635baf5dac 100644 --- a/src/Polly.Core/Timeout/OnTimeoutArguments.cs +++ b/src/Polly.Core/Timeout/OnTimeoutArguments.cs @@ -1,5 +1,3 @@ -using Polly.Strategy; - namespace Polly.Timeout; /// @@ -8,4 +6,4 @@ namespace Polly.Timeout; /// The context associated with the execution of a user-provided callback. /// The original exception that caused the timeout. /// The timeout value assigned. -public readonly record struct OnTimeoutArguments(ResilienceContext Context, Exception Exception, TimeSpan Timeout) : IResilienceArguments; +public readonly record struct OnTimeoutArguments(ResilienceContext Context, Exception Exception, TimeSpan Timeout); diff --git a/src/Polly.Core/Timeout/TimeoutGeneratorArguments.cs b/src/Polly.Core/Timeout/TimeoutGeneratorArguments.cs index cef7492faae..feb3ef8561d 100644 --- a/src/Polly.Core/Timeout/TimeoutGeneratorArguments.cs +++ b/src/Polly.Core/Timeout/TimeoutGeneratorArguments.cs @@ -1,9 +1,7 @@ -using Polly.Strategy; - namespace Polly.Timeout; /// /// Arguments used by the timeout strategy to retrieve a timeout for current execution. /// /// The context associated with the execution of a user-provided callback. -public readonly record struct TimeoutGeneratorArguments(ResilienceContext Context) : IResilienceArguments; +public readonly record struct TimeoutGeneratorArguments(ResilienceContext Context); diff --git a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs index 87eabfa42a3..d1708279523 100644 --- a/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs +++ b/src/Polly.Core/Timeout/TimeoutResilienceStrategy.cs @@ -61,7 +61,7 @@ protected internal override async ValueTask> ExecuteCoreAsync + OnOpened = async args => { args.Context.Properties.GetValue(PollyDependencyInjectionKeys.ServiceProvider, null!).Should().NotBeNull(); contextChecked = true; @@ -30,7 +30,7 @@ public async Task OnCircuitBreakWithServiceProvider_796() // do asynchronous call await Task.Yield(); }, - ShouldHandle = (outcome, _) => outcome.Result switch + ShouldHandle = args => args.Result switch { string result when result == "error" => PredicateResult.True, _ => PredicateResult.False diff --git a/src/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs b/src/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs index c45db6ddd5d..acaea09e454 100644 --- a/src/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs +++ b/src/Polly.Extensions.Tests/Telemetry/ResilienceTelemetryDiagnosticSourceTests.cs @@ -169,8 +169,8 @@ public void WriteEvent_MeteringWithEnrichers_Ok(bool noOutcome) context.Outcome!.Value.Result.Should().Be(true); } - context.ResilienceContext.Should().NotBeNull(); - context.ResilienceArguments.Should().BeOfType(); + context.Context.Should().NotBeNull(); + context.Arguments.Should().BeOfType(); context.Tags.Add(new KeyValuePair("custom-1", "custom-1-value")); }); @@ -221,12 +221,21 @@ private ResilienceTelemetryDiagnosticSource Create(Action? outcome, string? strategyKey = "my-strategy-key", ResilienceContext? context = null) { + context ??= ResilienceContext.Get(); var props = new ResilienceProperties(); if (!string.IsNullOrEmpty(strategyKey)) { props.Set(new ResiliencePropertyKey("StrategyKey"), strategyKey); } - telemetry.ReportEvent("my-event", "my-builder", props, "my-strategy", "my-strategy-type", new TestArguments(context), outcome); + telemetry.ReportEvent( + "my-event", + "my-builder", + props, + "my-strategy", + "my-strategy-type", + context, + outcome, + new TestArguments()); } } diff --git a/src/Polly.Extensions/Telemetry/EnrichmentContext.Pool.cs b/src/Polly.Extensions/Telemetry/EnrichmentContext.Pool.cs index c249ba29a74..f883f83ba06 100644 --- a/src/Polly.Extensions/Telemetry/EnrichmentContext.Pool.cs +++ b/src/Polly.Extensions/Telemetry/EnrichmentContext.Pool.cs @@ -10,16 +10,16 @@ public partial class EnrichmentContext static context => { context.Outcome = null; - context.ResilienceContext = null!; + context.Context = null!; context.Tags.Clear(); return true; }); - internal static EnrichmentContext Get(ResilienceContext resilienceContext, IResilienceArguments? arguments, Outcome? outcome) + internal static EnrichmentContext Get(ResilienceContext resilienceContext, object? arguments, Outcome? outcome) { var context = ContextPool.Get(); - context.ResilienceContext = resilienceContext; - context.ResilienceArguments = arguments; + context.Context = resilienceContext; + context.Arguments = arguments; context.Outcome = outcome; return context; diff --git a/src/Polly.Extensions/Telemetry/EnrichmentContext.cs b/src/Polly.Extensions/Telemetry/EnrichmentContext.cs index 3fffd41c9f0..b760c0fb3d9 100644 --- a/src/Polly.Extensions/Telemetry/EnrichmentContext.cs +++ b/src/Polly.Extensions/Telemetry/EnrichmentContext.cs @@ -20,12 +20,12 @@ private EnrichmentContext() /// /// Gets the resilience arguments associated with the resilience event, if any. /// - public IResilienceArguments? ResilienceArguments { get; internal set; } + public object? Arguments { get; internal set; } /// /// Gets the resilience context associated with the operation that produced the resilience event. /// - public ResilienceContext ResilienceContext { get; internal set; } = null!; + public ResilienceContext Context { get; internal set; } = null!; /// /// Gets the tags associated with the resilience event. diff --git a/src/Polly.Extensions/Telemetry/EnrichmentUtil.cs b/src/Polly.Extensions/Telemetry/EnrichmentUtil.cs index 47bebb26e2e..18f6ad2d72a 100644 --- a/src/Polly.Extensions/Telemetry/EnrichmentUtil.cs +++ b/src/Polly.Extensions/Telemetry/EnrichmentUtil.cs @@ -11,7 +11,7 @@ public static void Enrich( List> enrichers, ResilienceContext resilienceContext, Outcome? outcome, - IResilienceArguments? resilienceArguments) + object? resilienceArguments) { if (enrichers.Count == 0) { diff --git a/src/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs b/src/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs index 96d69b624ac..32217b05ffd 100644 --- a/src/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs +++ b/src/Polly.RateLimiting.Tests/RateLimiterResilienceStrategyTests.cs @@ -47,7 +47,7 @@ public void Execute_HappyPath() public void Execute_LeaseRejected(bool hasEvents, bool hasRetryAfter) { _diagnosticSource.Setup(v => v.IsEnabled("OnRateLimiterRejected")).Returns(true); - _diagnosticSource.Setup(v => v.Write("OnRateLimiterRejected", It.Is(obj => obj is IResilienceArguments))); + _diagnosticSource.Setup(v => v.Write("OnRateLimiterRejected", It.Is(obj => obj != null))); object? metadata = hasRetryAfter ? TimeSpan.FromSeconds(123) : null; diff --git a/src/Polly.RateLimiting/OnRateLimiterRejectedArguments.cs b/src/Polly.RateLimiting/OnRateLimiterRejectedArguments.cs index 5c42ead45a4..ba96fdebde3 100644 --- a/src/Polly.RateLimiting/OnRateLimiterRejectedArguments.cs +++ b/src/Polly.RateLimiting/OnRateLimiterRejectedArguments.cs @@ -1,5 +1,4 @@ using System.Threading.RateLimiting; -using Polly.Strategy; namespace Polly.RateLimiting; @@ -9,4 +8,4 @@ namespace Polly.RateLimiting; /// The context associated with the execution of a user-provided callback. /// The lease that has no permits and was rejected by the rate limiter. /// The amount of time to wait before retrying again. This value is retrieved from the by reading the . -public readonly record struct OnRateLimiterRejectedArguments(ResilienceContext Context, RateLimitLease Lease, TimeSpan? RetryAfter) : IResilienceArguments; +public readonly record struct OnRateLimiterRejectedArguments(ResilienceContext Context, RateLimitLease Lease, TimeSpan? RetryAfter); diff --git a/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs b/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs index c3b0f91a083..0d3fd70f422 100644 --- a/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs +++ b/src/Polly.RateLimiting/RateLimiterResilienceStrategy.cs @@ -44,7 +44,7 @@ protected override async ValueTask> ExecuteCoreAsync Context = context ?? ResilienceContext.Get(); - - public ResilienceContext Context { get; } } diff --git a/src/Polly.TestUtils/TestUtilities.cs b/src/Polly.TestUtils/TestUtilities.cs index 5c80da52c37..e8240a2c44f 100644 --- a/src/Polly.TestUtils/TestUtilities.cs +++ b/src/Polly.TestUtils/TestUtilities.cs @@ -41,7 +41,7 @@ public static async Task AssertWithTimeoutAsync(Func assertion, TimeSpan t public static ResilienceStrategyTelemetry CreateResilienceTelemetry(DiagnosticSource source) => new(new ResilienceTelemetrySource("dummy-builder", new ResilienceProperties(), "strategy-name", "strategy-type"), source); - public static ResilienceStrategyTelemetry CreateResilienceTelemetry(Action callback) + public static ResilienceStrategyTelemetry CreateResilienceTelemetry(Action callback) => new(new ResilienceTelemetrySource("dummy-builder", new ResilienceProperties(), "strategy-name", "strategy-type"), new CallbackDiagnosticSource(callback)); public static ILoggerFactory CreateLoggerFactory(out FakeLogger logger) @@ -84,11 +84,17 @@ public static void ReportEvent( ResilienceProperties builderProperties, string strategyName, string strategyType, - IResilienceArguments arguments, - Outcome? outcome) + ResilienceContext context, + Outcome? outcome, + object arguments) #pragma warning restore S107 // Methods should not have too many parameters { - source.Write(eventName, new TelemetryEventArguments(new ResilienceTelemetrySource(builderName, builderProperties, strategyName, strategyType), eventName, arguments, outcome)); + source.Write(eventName, new TelemetryEventArguments( + new ResilienceTelemetrySource(builderName, builderProperties, strategyName, strategyType), + eventName, + context, + outcome, + arguments)); } public static ResilienceContext WithResultType(this ResilienceContext context) @@ -99,9 +105,9 @@ public static ResilienceContext WithResultType(this ResilienceContext context private sealed class CallbackDiagnosticSource : DiagnosticSource { - private readonly Action _callback; + private readonly Action _callback; - public CallbackDiagnosticSource(Action callback) => _callback = callback; + public CallbackDiagnosticSource(Action callback) => _callback = callback; public override bool IsEnabled(string name) => true;