Let me alter your two operations a bit for the sake of simplicity.
static Random rnd = new();
static async Task<int?> OperationA()
{
await Task.Delay(100); //Simulate I/O call's latency
var number = rnd.Next();
return number % 5 == 0 ? number : null;
}
static async Task<int?> OperationB()
{
await Task.Delay(100); //Simulate I/O call's latency
var number = rnd.Next();
return number % 4 == 0 ? number + 1 : null;
}
- We have two operations (
OperationA
and OperationB
) and they will either return null
or an int
- Their implementation are not important, they just serve demonstration purposes
Now If you need to define a retry policy which
- Triggers if both operations failed
- Triggers at most 5 times
- Delays 200 milliseconds between each attempt
then you can do that like this:
var retryUntilGetResult = Policy<(int?, int?)>
.HandleResult(((int? OpA, int? OpB) results) => !results.OpA.HasValue && !results.OpB.HasValue)
.WaitAndRetryAsync(retryCount: 5, _ => TimeSpan.FromMilliseconds(200));
- The
Policy<(int?, int?)>
part says that your return type is a tuple with two nullable integers
- The
HandleResult
part says that if the returned tuple (results
) does not contain any integer (!...HasValue
) then the policy should trigger
- The
WaitAndRetryAsync
part says you want to decorate an async method with this policy
- The policy should fire at most 5 times, but wait 200 milliseconds between each attempt
The usage of this policy looks like this:
var result = await retryUntilGetResult.ExecuteAsync(async () =>
{
Task<int?> opA = OperationA();
Task<int?> opB = OperationB();
await Task.WhenAll(opA, opB);
return (await opA, await opB);
});
- We fire of two Tasks and we are waiting for both to complete
- Then we return a new tuple where we pass the results of the async operations
For the sake of completeness here the full source code:
static async Task Main()
{
var retryUntilGetResult = Policy<(int?, int?)>
.HandleResult(((int? OpA, int? OpB) results) => !results.OpA.HasValue && !results.OpB.HasValue)
.WaitAndRetryAsync(retryCount: 5, _ => TimeSpan.FromMilliseconds(200));
var result = await retryUntilGetResult.ExecuteAsync(async () =>
{
Task<int?> opA = OperationA();
Task<int?> opB = OperationB();
await Task.WhenAll(opA, opB);
return (await opA, await opB);
});
Console.WriteLine(result);
}
static Random rnd = new();
static async Task<int?> OperationA()
{
await Task.Delay(100); //Simulate I/O call's latency
var number = rnd.Next();
return number % 5 == 0 ? number : null;
}
static async Task<int?> OperationB()
{
await Task.Delay(100); //Simulate I/O call's latency
var number = rnd.Next();
return number % 4 == 0 ? number + 1 : null;
}