I might be late to the party but let me share my wisdom.
When you define a policy then you have to know upfront that you want to decorate a sync or an async method/function.
Retry policy for sync method
Policy
.Handle<Exception>()
.RetryForever();
Retry policy for async method
Policy
.Handle<Exception>()
.RetryForeverAsync();
- In the former case you have an
Execute method which could anticipate
- an
Action (a method without return value)
- or a
Func<TResult> (a method with TResult typed return value)
- and there are a dozen of other overloads
- In the latter case you have an
ExecuteAsync method which could anticipate
- a
Func<Task> (an async method without return value)
- or a
Func<Task<TResult>> (an async method with TResult typed return value)
- and there are a dozen of other overloads
Retry policy for sync function
Policy<int>
.Handle<Exception>()
.RetryForever();
Retry policy for async function
Policy<int>
.Handle<Exception>()
.RetryForeverAsync();
In these cases your to be decorated code should return an int.
So the Execute anticipates a Func<int> delegate
policy.Execute(() => { ... return 42; });
and the ExecuteAsync anticipates a Func<Task<int>> delegate
await policy.Execute(async () => { ... return Task.FromResult(42); });
Back to your code
Since you did not made any constraint on the return type during the policy definition that's why you could pass a Func which returns a Task (Execute(async () => { ...});).
So, your Execute returns a Task which is not awaited.
Let's await it
await _retryPolicy.Execute(async () =>
{
Console.WriteLine(i);
i++;
int.Parse("something");
});
If you await the returned Task then it will throw a FormatException.
But that Exception is thrown by the await which is outside of the Execute's delegate. So, it will NOT trigger a retry.
Option A for fix
_retryPolicy.Execute(() =>
{
Console.WriteLine(i);
i++;
int.Parse("something");
});
By removing async, your delegate will be an Action which will throw the FormatException which could trigger the retry policy.
Option B for fix
var _retryPolicy = Policy<int>
.Handle<Exception>()
.RetryForeverAsync();
await _retryPolicy.ExecuteAsync(async () =>
{
Console.WriteLine(i);
i++;
int.Parse("something");
});
If you define your policy for async method and you await the ExecuteAsync then it will work as well. Why? Because ExecuteAsync awaits the provided delegate even if you did not define the delegate as async
await _retryPolicy.ExecuteAsync(() =>
{
Console.WriteLine(i);
i++;
int.Parse("something");
return Task.CompletedTask; //This code is never reached
});