Polly 回退操作没有抛出指定的异常。发生了什么?

Polly fallback action did not throw the specified exception. What happened?

鉴于此代码尝试调用 AuthenticationManager.Authenticate() 10 次,然后在 10 次尝试失败后抛出 CustomException

string res = Policy
    .Handle<Exception>() // Handles any exception
    .OrResult<string>(result => string.IsNullOrWhiteSpace(result)) // or if result is null
    .Fallback(() => throw new CustomException("Exception!")) // Not being thrown after 10 unsuccessful attempts
    .Wrap(
        Policy
            .Handle<Exception>()
            .OrResult<string>(result => string.IsNullOrWhiteSpace(result))
            .WaitAndRetry(
                10,
                retryAttempt => TimeSpan.FromSeconds(60),
                onRetry: (response, delay, retryCount, context) => Trace.WriteLine($"[{DateTime.UtcNow}] Authentication failed. Retrying after 60 seconds...(Attempt {retryCount} of 10)")))
    .ExecuteAndCapture(() => AuthenticationManager.Authenticate())
    .Result;

为什么 CustomException 没有从回退操作中抛出?正确的做法应该是什么?

这些单元测试通过:(xUnit)

[Fact]
public void Test1()
{
    var policy = Policy<string>
        .Handle<Exception>()
        .OrResult(result => string.IsNullOrWhiteSpace(result))
        .Fallback(() => throw new CustomException())
        .Wrap(
              Policy<string>
                 .Handle<Exception>()
                 .OrResult(result => string.IsNullOrWhiteSpace(result))
                 .WaitAndRetry(
                  10,
                  _ => TimeSpan.FromSeconds(1),
                  onRetry: (response, delay, retryCount, context) => Trace.WriteLine($"[{DateTime.UtcNow}] Authentication failed. Retrying after 1 second...(Attempt {retryCount} of 10)")
                  )
        );

    var result = policy.ExecuteAndCapture(() => throw new Exception("Muh"));
    Assert.IsType<CustomException>(result.FinalException);
    Assert.Null(result.Result);
    Assert.Equal(OutcomeType.Failure, result.Outcome);
}

[Fact]
public void Test2()
{
    var policy = Policy<string>
        .Handle<Exception>()
        .OrResult(result => string.IsNullOrWhiteSpace(result))
        .Fallback(() => throw new CustomException())
        .Wrap(
              Policy<string>
                 .Handle<Exception>()
                 .OrResult(result => string.IsNullOrWhiteSpace(result))
                 .WaitAndRetry(
                  10,
                  _ => TimeSpan.FromSeconds(1),
                  onRetry: (response, delay, retryCount, context) => Trace.WriteLine($"[{DateTime.UtcNow}] Authentication failed. Retrying after 1 second...(Attempt {retryCount} of 10)")
                  )
        );

    Assert.Throws<CustomException>(() => policy.Execute(() => throw new Exception("Muh")));
}

所以,我想,您可以只使用 Execute,或者像这样检查结果并重新抛出异常:

var result = policy.ExecuteAndCapture(() => AuthenticationManager.Authenticate());
if( result.Outcome == OutcomeType.Failure && result.FinalException is not null)
{
    throw result.FinalException;
}
return result.Result;

发现仅使用 WaitAndRetry 并检查 Outcome 对我有用(以防有人遇到这个问题):

var policyResult = Policy
    .Handle<Exception>()
    .OrResult<AuthenticationResult>(result => result is null)
        .WaitAndRetry(
            10,
            retryAttempt => TimeSpan.FromSeconds(60),
            onRetry: (response, delay, retryCount, context) => Trace.WriteLine($"[{DateTime.UtcNow}] Call failed. Retrying after 60 seconds...(Attempt {retryCount} of 10)"))
    .ExecuteAndCapture(() => AuthenticationManager.Authenticate());

if (policyResult.Outcome == OutcomeType.Failure)
{
    throw new CustomException("FAILED", policyResult.FinalException);
}

else
{
    string value = policyResult.FinalHandledResult;
}