Often, we have transient problems in our application, such as a network failure, system rebooting, or anything else that throws an exception. In this article, we are going to learn how to implement retry logic in C# to help us handle these problems.
Let’s start.
Simulate a Failure in Our Application
Before we create the retry logic in C#, we need a method that can create a transient problem. That said, let’s create a very basic logic to simulate the failure:
public static void FirstSimulationMethod() { const int forbiddenNumber = 3; Console.Write("Write a number: "); var number = int.Parse(Console.ReadLine() ?? "0"); if (number == forbiddenNumber) throw new ArgumentException($"The generated number must be different from {forbiddenNumber}"); Console.Write("Not Equal"); }
First, this method asks for a number. After parsing this number into an integer, it compares this number to a forbidden number we previously set.
If the input number is equal to the forbidden number, this method is going to throw an exception with a message, otherwise, it prints the “Not Equal” message in the console.
This exception simulates a transient problem that may occur by a brief network failure or something like this.
Creating the Second Method
Let’s inspect the second method:
public static int SecondSimulationMethod() { const int forbiddenNumber = 3; Console.Write("Write a number: "); var number = int.Parse(Console.ReadLine() ?? "0"); if (number == forbiddenNumber) throw new ArgumentException($"The generated number must be different from {forbiddenNumber}"); Console.Write("Not Equal"); return number; }
Note that this method is very similar to the FirstSimulationMethod
, however, in the end, it returns the input value. This method is important to show how to implement retry logic using Action
or Func
delegates.
In case you are not familiar with the Action and Func delegates, or delegates overall, you can learn more here.
Implementing the Retry Logic in C#
Once we have the methods to simulate the transient problems, we can focus on writing the retry logic in C#. Let’s create an Executor
static class with an Execute
method:
public static class Executor { public static void Execute(Action action, int numberOfRetries) { var tries = 0; while (tries <= numberOfRetries) { try { action(); return; } catch { tries++; } } throw new RetryException($"Error after {tries} tries"); } }
The Execute
method is responsible to execute the logic several times if there’s any problem. It receives an Action
as a first parameter and the number of times we want to retry (numberOfRetries
) as a second parameter.
Then, we need to loop and execute the method until the tries
variable value is lower or equal to the numberOfRetries
variable value. If the Action executes successfully, the retry logic finishes its execution. However, in case of exception, it increments the tries
variable and retries to execute the Action
.
When the tries
value is greater or equal than the numberOfRetries
, it finishes the execution and throws an exception with some message.
Retry Logic in C# With the Func Delegate
Now, let’s create a new overload of the Execute
method:
public static TResult? Execute<TResult>(Func<TResult> func, int numberOfRetries) { var tries = 0; while (tries <= numberOfRetries) { try { return func(); } catch { tries++; } } throw new RetryException($"Error after {tries} tries"); }
Note that the main difference is that this time this is a generic method with a return type.
Once it has a return type, instead of receiving an Action
as a parameter, this method receives a Func
, and then, we return the result of this Func
.
With both previous methods, we can use this retry logic in C# for both, Action
and Func
delegates.
Using the Executor Class
Once we have defined the Executor
class and its methods, it is time to execute the FirstSimulationMethod
and the SecondSimulationMethod
methods.
Let’s check it:
Executor.Execute(FirstSimulationMethod, 3);
We call the Execute
method under the Executor
class passing the method we want to execute, and the maximum number of retries as parameters.
Now, let’s use the overload method:
var result = Executor.Execute(SecondSimulationMethod, 3);
This time, we pass a Func
as the first parameter. That said, we need to declare a result
variable to receive the return from this Func
.
It is good to mention that when we send 3 as the number of retries, this method is going to execute 4 times. The first execution and three retries.
Retry Logic in C# Using Polly
Polly is a NuGet Package that allows us to handle transient problems. We often use it to create resilient microservices.
To use Polly, first, we need to install the Polly NuGet Package:
Install-Package Polly
Once we have Polly installed, let’s check how to use its retry feature:
Policy .Handle<ArgumentException>() .Retry(3) .Execute(FirstSimulationMethod);
First, we need to use the Handle
generic method inside the Policy
static class to tell Polly which kind of exception we are expecting. In case we are expecting multiple exceptions, after the Handle
method, we can use the Or<>()
method to specify the other exception we want to handle.
Next, we use the Retry
method passing an integer as a parameter that represents the number of times we want to retry.
Finally, we call the Execute
method and pass an Action
or a Func
we want to execute as a parameter.
Conclusion
In this article, we’ve implemented our basic retry logic to use in our daily routine. Also, we have seen how to use Polly to achieve the same result. Among all Polly’s features, in this article, we covered the retry policy, however, we highly recommend looking at Polly’s GitHub Source Code and Polly’s Wiki to see all the other features it contains.