The DotNetExtras.Retry library provides simple methods for recovering from and retrying failed operations. For the detailed description of the library API, code samples, and usage scenarios, see the API documentation section.
The DotNetExtras.Retry library handles three basic cases:
A failed operation can recover after reloading configuration settings (or doing whatever preparation is required) and a single retry (the number of retry attempts can be adjusted, but generally, if one retry does not help here, there is little sense in retrying more).
A failed operation can recover after one or more retries until the retry timeout is reached.
A failed operation can recover after one or more retries until the maximum number of attempts is reached.
The most common real-life scenarios that can be addressed by the DotNetExtras.Retry library include:
The code calls an external API with a client secret that expired and needs to be refreshed from the configuration source (secret vault, password safe, etc.).
The code calls an external API that fails because it waits for a background operation to complete, and the SLA for the background job is two minutes, so the code will keep retrying the operation for a couple of minutes.
The code calls an external API that fails occasionally for unclear reasons (could be a timeout or what not), but it normally succeeds on a repeated call.
The library implements retries as static Execute.WithRetry methods that take a delegate (code block) as a parameter. The delegate is executed, and if it fails, the library will retry the failed code block until the operation succeeds or it reaches the stop condition. The stop condition can be based on the maximum number of attempts or a timeout. The caller may specify a delay between the retry attempts.
To detect a failure that requires a retry, the Execute.WithRetry methods use the type of exception thrown by the delegate (code block).
In the scenarios where the operation can recover after reloading configuration settings, an Execute.WithRetry method needs an object that implements the single Reload method of the IReloadable interface (this method reinitializes settings or does whatever else needs to be done to address the error).
The following example illustrates how to detect a failure that may be caused by an old configuration setting, reload the settings, and retry the operation.
using DotNetExtras.Retry;
...
// This class implements the Reload() method of the IReloadable interface,
// in which it reloads the configuration settings that could have changed.
ReloadableService service = new();
// If the operation throws a NotSupportedException,
// reload the service object and retry the operation one more time.
// The operation is expected to return an int value.
int result = Execute.WithRetry<NotSupportedException, int>(() =>
{
// BEGINNING OF THE CODE BLOCK THAT WILL BE RETRIED.
try
{
// Attempt to perform the operation.
return service.DoSomething();
}
// This is not the expected exception for the retry,
// but...
catch (InvalidOperationException ex)
{
// ...we can simulate the expected exception for an appropriate condition.
if (ex.Message.StartsWith("Unexpected"))
{
throw new NotSupportedException("Simulated exception triggering a reload.", ex);
}
// This will handle both the expected exception
// leading to a reload and retry (one retry attempt only),
// as well as an unhandled exception that will
// result in error.
throw;
}
// END OF THE CODE BLOCK THAT WILL BE RETRIED.
}, service);
// We are passing the same service object here because it is the one that
// implements the reload method, but it can be a different object.
// We use the defaults for the delay (no delay) and the maximum attempts (2).
For the complete example and other samples covering additional scenarios, see the source code (and read the comments) of the demo project.