diff --git a/src/Polly.Core/ResilienceStrategy.DebuggerProxy.cs b/src/Polly.Core/ResilienceStrategy.DebuggerProxy.cs new file mode 100644 index 00000000000..c81abf44f57 --- /dev/null +++ b/src/Polly.Core/ResilienceStrategy.DebuggerProxy.cs @@ -0,0 +1,35 @@ +namespace Polly; + +public abstract partial class ResilienceStrategy +{ + internal sealed class DebuggerProxy + { + private readonly ResilienceStrategy _resilienceStrategy; + + public DebuggerProxy(ResilienceStrategy resilienceStrategy) => _resilienceStrategy = resilienceStrategy; + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public IEnumerable Strategies => UnwrapStrategies(_resilienceStrategy); + + private IEnumerable UnwrapStrategies(ResilienceStrategy strategy) + { + if (strategy is ResilienceStrategyPipeline pipeline) + { + return pipeline.Strategies; + } + + if (strategy is ReloadableResilienceStrategy reloadableResilienceStrategy) + { + var list = new List + { + strategy + }; + list.AddRange(UnwrapStrategies(reloadableResilienceStrategy.Strategy)); + + return list; + } + + return new[] { strategy }; + } + } +} diff --git a/src/Polly.Core/ResilienceStrategy.TResult.Async.cs b/src/Polly.Core/ResilienceStrategy.TResult.Async.cs index 42b1181083c..8d9bafd51f8 100644 --- a/src/Polly.Core/ResilienceStrategy.TResult.Async.cs +++ b/src/Polly.Core/ResilienceStrategy.TResult.Async.cs @@ -1,3 +1,5 @@ +using Polly.Utils; + namespace Polly; /// @@ -8,6 +10,7 @@ namespace Polly; /// Resilience strategy supports various types of callbacks of result type /// and provides a unified way to execute them. This includes overloads for synchronous and asynchronous callbacks. /// +[DebuggerTypeProxy(typeof(ResilienceStrategy<>.DebuggerProxy))] public partial class ResilienceStrategy { internal ResilienceStrategy(ResilienceStrategy strategy) => Strategy = strategy; diff --git a/src/Polly.Core/ResilienceStrategy.TResult.DebuggerProxy.cs b/src/Polly.Core/ResilienceStrategy.TResult.DebuggerProxy.cs new file mode 100644 index 00000000000..44eff368d65 --- /dev/null +++ b/src/Polly.Core/ResilienceStrategy.TResult.DebuggerProxy.cs @@ -0,0 +1,14 @@ +namespace Polly; + +public partial class ResilienceStrategy +{ + internal sealed class DebuggerProxy + { + private readonly ResilienceStrategy.DebuggerProxy _proxy; + + public DebuggerProxy(ResilienceStrategy strategy) => _proxy = new ResilienceStrategy.DebuggerProxy(strategy.Strategy); + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public IEnumerable Strategies => _proxy.Strategies; + } +} diff --git a/src/Polly.Core/ResilienceStrategy.cs b/src/Polly.Core/ResilienceStrategy.cs index 6ab5449f124..ac415d04e6b 100644 --- a/src/Polly.Core/ResilienceStrategy.cs +++ b/src/Polly.Core/ResilienceStrategy.cs @@ -9,6 +9,7 @@ namespace Polly; /// Resilience strategy supports various types of callbacks and provides a unified way to execute them. /// This includes overloads for synchronous and asynchronous callbacks, generic and non-generic callbacks. /// +[DebuggerTypeProxy(typeof(DebuggerProxy))] public abstract partial class ResilienceStrategy { /// diff --git a/src/Polly.Core/Utils/ResilienceStrategyPipeline.cs b/src/Polly.Core/Utils/ResilienceStrategyPipeline.cs index 37b34225b0a..e483d3c043c 100644 --- a/src/Polly.Core/Utils/ResilienceStrategyPipeline.cs +++ b/src/Polly.Core/Utils/ResilienceStrategyPipeline.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; + namespace Polly.Utils; #pragma warning disable S2302 // "nameof" should be used @@ -5,6 +7,7 @@ namespace Polly.Utils; /// /// A pipeline of strategies. /// +[DebuggerDisplay("ResilienceStrategyPipeline, Strategies = {Strategies.Count}")] internal sealed class ResilienceStrategyPipeline : ResilienceStrategy { private readonly ResilienceStrategy _pipeline; diff --git a/test/Polly.Core.Tests/ResilienceStrategyTests.cs b/test/Polly.Core.Tests/ResilienceStrategyTests.cs index 81ec8d87a81..2e5e52dd56e 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyTests.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyTests.cs @@ -1,3 +1,6 @@ +using Moq; +using Polly.Utils; + namespace Polly.Core.Tests; public partial class ResilienceStrategyTests @@ -28,6 +31,22 @@ await TestUtilities.AssertWithTimeoutAsync(async () => }); } + [Fact] + public void DebuggerProxy_Ok() + { + var pipeline = ResilienceStrategyPipeline.CreatePipeline(new[] + { + new TestResilienceStrategy(), + new TestResilienceStrategy() + }); + var reloadable = new ReloadableResilienceStrategy(pipeline, () => default, () => pipeline, TestUtilities.CreateResilienceTelemetry(Mock.Of())); + + new ResilienceStrategy.DebuggerProxy(NullResilienceStrategy.Instance).Strategies.Should().HaveCount(1); + new ResilienceStrategy.DebuggerProxy(pipeline).Strategies.Should().HaveCount(2); + new ResilienceStrategy.DebuggerProxy(reloadable).Strategies.Should().HaveCount(3); + new ResilienceStrategy.DebuggerProxy(NullResilienceStrategy.Instance).Strategies.Should().HaveCount(1); + } + public class ExecuteParameters : ExecuteParameters { public ExecuteParameters(Func> execute, T resultValue)