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 await
ed.
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
await
s 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
});