Retrying in C#
Retrying if an exception occurs is a very common scenario that many of you may have faced one time or the other. Let's take an example. Let's say I have a dll in which there is a class Pinger. It has methods like Ping(), WaitAndPing(int), WaitAndPingConditional(bool, int).Let's assume that these methods Ping a certain server and may sometimes throw a TimedOutException if the server is busy. Now in such a case, we can write a wrapper class over this, wrapping each method, where we catch the exception and retry once more. The code might look like
private bool UsualMethod3(bool a, int b)
{
Pinger pinger = new Pinger();
for (int i = 0; i < 2; i++)
{
try
{
return pinger.Ping(a, b);
}
catch (Exception e)
{
if (i == 0 && e is TimeoutException)
{
Console.WriteLine(string.Format("Got an exception \"{0}\" in attempt {1}, retrying", e.Message, i + 1));
continue;
}
throw e;
}
}
return null;
}
The problem with the above code is that you have to repeat the same for every method of PingerClass. If there are several such methods, very quickly we might end up having a lot of duplication of the same try catch and for loop. A simple solution for this is to have a method that takes any kind of function as an input and does retry etc for this. This can be achieved using Func. So, if you check the attached code, you will find that I have a method like this.
public object Retry(Func<object> function, Action<Exception> retryHandler, int retryCount).
So anywhere you need to do a retry, you just pass the function or a chunk of code wrapped in a function to it, You will be able to execute any kind of code in this retry block.
RetryManager.Instance.Retry(() =>
{
//Write whatever code you want to be retried here. For the above case, you would be making a call to pinger.Ping() here
}, null, 1);
If you want to handle some kind of exceptions only, then you can write code in the handler.
RetryManager.Instance.Retry(() =>
{
//Write whatever code you want to be retried here. For the above case, you would be making a call to pinger.Ping() here
},
(e) =>
{
//Write code to handle a failure here before the retry happens. For the above case, it would be if(!(e isTimeoutException)) throw e;
}, 1);
To use the attached solution, unzip it, build the Pinger class first(because it is used as a dll ref in the test project) and then look at the test code. There are two testmethods RetryTestsUsual and RetryUsingRetryHelper. In RetryTestsUsual, retrying is done using a for loop and a try catch. As said above this is a simple solution if are doing it for only one or two methods. In RetryUsingRetryHelper, you will find that the code has been significantly reduced and there is a lot less duplication.