Retry Pattern in C#

Retry Pattern in C# using Asynchronous Programming with Examples

In this article, I am going to discuss Retry Pattern in C# using Asynchronous Programming with Examples. Please read our previous article where we discussed How to Create Synchronous Method using Task in C# with Examples.

Retry Pattern in C# using Asynchronous Programming

One application of asynchronous programming is to perform a retry pattern. The idea is that sometimes there will be operations that will want to retry several times. However, we do not want to retry immediately, but we want to retry after a certain amount of time. For example, if we make an HTTP request to a Web server, sometimes those operations fail and we may not want immediately to tell the user that there was an error. We may want to retry the operation just in case the operation this time works.

Structure of Retry Pattern in C#:

The following image shows the basic structure of the Retry Pattern in C# Asynchronous Programming.

Structure of Retry Pattern in C#

Here, the variable RetryTimes tells the number of times we will retry the operation if it is failing. If it is not failing then we will not retry. And, we have set the value to 3 which means it will retry the operation maximum of 3 times.

And one more thing, we don’t want to immediately retry the operation. We may want to retry the operation after a certain amount of time. Here, the parameter WaitTime specifies the time duration for the retrying operation. We have set the value of WaitTime to 500 milliseconds, so it will retry the operation after 500 milliseconds or half a second.

Then we have created the for loop using the try-catch block. This for loop will execute a minimum of 1 time and a maximum of 3 times as we set the RetryTimes value as 3.

Then inside the catch block, we will call our async operation. The operation may be an API call or an Async method call. If the operation is successful we will break the loop and will come out from the for loop. If the operation is not successful meaning if we are getting any exception from the API, or from the Async method (whatever may be the operation), then the catch block will handle that exception and executes the catch block. If you want, then you can log the exception details, then wait for 500 milliseconds before continuing the next iteration of the loop.

Example to understand Retry Pattern in C#:

The following example shows the Retry Pattern in C# using Asynchronous Programming.

using System;
using System.Threading.Tasks;

namespace AsynchronousProgramming
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main Method Started");

            RetryMethod();
            
            Console.WriteLine("Main Method Completed");
            Console.ReadKey();
        }

        public static async void RetryMethod()
        {
            //It tells the number of times we will retry the operation if it is failing
            //Of course, if it is not falling then we will not retry
            var RetryTimes = 3;

            //The idea is that we don't want to immediately retry, but 
            //we may want to retry after a certain amount of time.
            //In our case, it is five hundred milliseconds or half a second.
            var WaitTime = 500;

            for (int i = 0; i < RetryTimes; i++)
            {
                try
                {
                    //Do the Operation
                    //If the Operation Successful break the loop
                    await RetryOperation();
                    Console.WriteLine("Operation Successful");
                    break;
                }
                catch (Exception Ex)
                {
                    //If the operations throws an error
                    //Log the Exception if you want
                    Console.WriteLine($"Retry {i+1}: Getting Exception : {Ex.Message}");
                    //Wait for 500 milliseconds
                    await Task.Delay(WaitTime);
                }
            }
        }
        
        public static async Task RetryOperation()
        {
            //Doing Some Processing
            await Task.Delay(500);

            //Throwing Exception so that retry will work
            throw new Exception("Exception Occurred in while Processing...");
        }
    }
}
Output:

Example to understand Retry Pattern in C#

Generic Retry Pattern in C# Asynchronous Programming:

In the previous example, we have seen how to create Retry Pattern in Asynchronous Programming. If we want to apply Retry Pattern at multiple places, then we need to make the retry pattern generic. Let us see how we can do this. Please have a look at the following image.

Generic Retry Pattern in C# Asynchronous Programming

The above retry pattern exactly does the same thing as the previous one. The only difference here is that this retry pattern can be used with multiple methods. Let us see an example for understanding. Please have a look at the below example. In the below example, using the generic retry pattern we are invoking the RetryOperation1 and RetryOperation2 async methods.

using System;
using System.Threading.Tasks;

namespace AsynchronousProgramming
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main Method Started");

            RetryMethod();
            
            Console.WriteLine("Main Method Completed");
            Console.ReadKey();
        }

        public static async void RetryMethod()
        {
            //It will retry 3 times, here the function is RetryOperation1
            await Retry(RetryOperation1);

            //It will retry 4 times, here the function is RetryOperation2
            await Retry(RetryOperation2,4);
        }

        //Generic Retry Method
        //Func is a generate delegate which returns something, in our case it is returning a Task
        //We are setting the default value for RetryTimes = 3 and WaitTime = 500 milliseconds
        public static async Task Retry(Func<Task> fun, int RetryTimes = 3, int WaitTime = 500)
        {
            for (int i = 0; i < RetryTimes; i++)
            {
                try
                {
                    //Do the Operation
                    //We are going to invoke whatever function the generic func delegate points to
                    await fun();
                    Console.WriteLine("Operation Successful");
                    break;
                }
                catch (Exception Ex)
                {
                    //If the operations throws an error
                    //Log the Exception if you want
                    Console.WriteLine($"Retry {i + 1}: Getting Exception : {Ex.Message}");
                    //Wait for 500 milliseconds
                    await Task.Delay(WaitTime);
                }
            }
        }

        public static async Task RetryOperation1()
        {
            //Doing Some Processing
            await Task.Delay(500);

            //Throwing Exception so that retry will work
            throw new Exception("Exception Occurred in RetryOperation1");
        }

        public static async Task RetryOperation2()
        {
            //Doing Some Processing
            await Task.Delay(500);

            //Throwing Exception so that retry will work
            throw new Exception("Exception Occurred in RetryOperation2");
        }
    }
}
Output:

Generic Retry Pattern Examples in C# Asynchronous Programming

Problems with the above Generic Retry Pattern?

In the above generic retry pattern, we have one problem. We are calling the Retry from the RetryMethod as follows:

await Retry(RetryOperation1);

Here, what if I want to do something if the operation fails three times? Because of the way we implemented the generic Retry method it just continues executing without telling us that the operation was successful or there was an error. Let us modify the Retry method as follows. Here, we are reducing the for loop execution for 1 time, so that we can execute the operation last time outside the for loop and this will work.

Problems with the above Generic Retry Pattern?

In the above code, we will get the RetryTimes value as 3, then the loop will execute 2 times if the operation was not successful. The last time will execute outside the for loop and we are not handling the exception here, so it will throw an exception that will tell the operation was successful. Now, you can catch the exception from where you called the Retry method as follows:

Generic Retry Pattern in C#

The complete example code is given below.

using System;
using System.Threading.Tasks;

namespace AsynchronousProgramming
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main Method Started");

            RetryMethod();
            
            Console.WriteLine("Main Method Completed");
            Console.ReadKey();
        }

        public static async void RetryMethod()
        {
            //It will retry 3 times, here the function is RetryOperation1
            try
            {
                await Retry(RetryOperation1);
            }
            catch(Exception ex)
            {
                Console.WriteLine("The Operation was Failed");
            }
        }

        //Generic Retry Method
        //Func is a generate delegate which returns something, in our case it is returning a Task
        //We are setting the default value for RetryTimes = 3 and WaitTime = 500 milliseconds
        public static async Task Retry(Func<Task> fun, int RetryTimes = 3, int WaitTime = 500)
        {
            //Reducing the for loop Exection for 1 time
            for (int i = 0; i < RetryTimes - 1; i++)
            {
                try
                {
                    //Do the Operation
                    //We are going to invoke whatever function the generic func delegate points to
                    await fun();
                    Console.WriteLine("Operation Successful");
                    break;
                }
                catch (Exception Ex)
                {
                    //If the operations throws an error
                    //Log the Exception if you want
                    Console.WriteLine($"Retry {i + 1}: Getting Exception : {Ex.Message}");
                    //Wait for 500 milliseconds
                    await Task.Delay(WaitTime);
                }
            }

            //Final try to execute the operation
            await fun();
        }

        public static async Task RetryOperation1()
        {
            //Doing Some Processing
            await Task.Delay(500);

            //Throwing Exception so that retry will work
            throw new Exception("Exception Occurred in RetryOperation1");
        }
    }
}

When you run the above code, you will get the following output. Here, you can see that we get two errors because the loop executes two times, and finally, we get that operation failed. This is because the final execution of the function is executed outside of the try-catch block.

Generic Retry Pattern in C# with Examples

Generic Async Retry Method with Returning Value in C#:

As of now, the way we implement the Generic Retry method is not returning any value. Now, let us create a generic Retry method to return a value. If you want to return a value then you need to use Task<T>. For a better understanding, please have a look at the below image. In the below, T represents the type of value that the operation is going to return.

Generic Async Retry Method with Returning Value in C#

In order to test the above Retry method, please create the following async method which returns a string.

public static async Task<string> RetryOperationValueReturning()
{
    //Doing Some Processing and return the value
    await Task.Delay(500);

    //Throwing Exception so that retry will work
    throw new Exception("Exception Occurred in RetryOperation1");
}

The Complete Example code is given below.

using System;
using System.Threading.Tasks;

namespace AsynchronousProgramming
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main Method Started");

            RetryMethod();
            
            Console.WriteLine("Main Method Completed");
            Console.ReadKey();
        }

        public static async void RetryMethod()
        {
            //It will retry 3 times, here the function is RetryOperation1
            try
            {
                var result = await Retry(RetryOperationValueReturning);
                Console.WriteLine(result);
            }
            catch(Exception ex)
            {
                Console.WriteLine("The Operation was Failed");
            }
        }
        
        //Generic Retry Method Returning Value
        //Func is a generate delegate which returns something, in our case it is returning a Task
        //We are setting the default value for RetryTimes = 3 and WaitTime = 500 milliseconds
        public static async Task<T> Retry<T>(Func<Task<T>> fun, int RetryTimes = 3, int WaitTime = 500)
        {
            //Reducing the for loop Exection for 1 time
            for (int i = 0; i < RetryTimes - 1; i++)
            {
                try
                {
                    //Do the Operation
                    //We are going to invoke whatever function the generic func delegate points to
                    //We will return from here if the operation was successful
                   return await fun();
                   
                }
                catch (Exception Ex)
                {
                    //If the operations throws an error
                    //Log the Exception if you want
                    Console.WriteLine($"Retry {i + 1}: Getting Exception : {Ex.Message}");
                    //Wait for 500 milliseconds
                    await Task.Delay(WaitTime);
                }
            }

            //Final try to execute the operation
           return await fun();
        }

        public static async Task<string> RetryOperationValueReturning()
        {
            //Doing Some Processing and return the value
            await Task.Delay(500);

            //Uncomment the below code to successfully return a string
            //return "Operation Successful";

            //Throwing Exception so that retry will work
            throw new Exception("Exception Occurred in RetryOperation1");
        }
    }
}
Output:

Generic Async Retry Method with Returning Value in C# with Examples

The following code will successfully run.

using System;
using System.Threading.Tasks;

namespace AsynchronousProgramming
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Main Method Started");

            RetryMethod();
            
            Console.WriteLine("Main Method Completed");
            Console.ReadKey();
        }

        public static async void RetryMethod()
        {
            try
            {
                var result = await Retry(RetryOperationValueReturning);
                Console.WriteLine(result);
            }
            catch(Exception ex)
            {
                Console.WriteLine("The Operation was Failed");
            }
        }

        public static async Task<T> Retry<T>(Func<Task<T>> fun, int RetryTimes = 3, int WaitTime = 500)
        {
            for (int i = 0; i < RetryTimes - 1; i++)
            {
                try
                {
                   return await fun();
                }
                catch (Exception Ex)
                {
                    Console.WriteLine($"Retry {i + 1}: Getting Exception : {Ex.Message}");
                    await Task.Delay(WaitTime);
                }
            }
           return await fun();
        }

        public static async Task<string> RetryOperationValueReturning()
        {
            await Task.Delay(500);
            return "Operation Successful";
        }
    }
}
Output:

Retry Pattern in C# using Asynchronous Programming with Examples

So, we have implemented a Retry Pattern that allows us to centralize the logic of repeating an operation several times until it works or until we run out of retry trials.

In the next article, I am going to discuss the Only One Pattern in C# Asynchronous Programming with Examples. Here, in this article, I try to explain Retry Pattern in C# Asynchronous Programming with Examples. I hope you enjoy this Retry Pattern in C# using Asynchronous Programming with Examples article.

1 thought on “Retry Pattern in C#”

  1. blank

    a little mistake in the “Problems with the above Generic Retry Pattern?” code.
    Inside the for loop, if the task succeeds, we must return and not break. Because if it succeeds, it breaks the for loop, but the task is redone outside the for loop (the code is good in the generic code example though, because we can clearly see that it returns if the task succeeds)

Leave a Reply

Your email address will not be published.