Retry loop for asynchronous HTTP requests

I've got a method that needs to PUT data to a web API. Sometimes the connection fails, so I needed a way to do retries, but if the retries fail, I still need to capture the exception and re-throw it.

I've got something that I "believe" is working, but I've got a stupid throw at the end. Is there a better, more concise way to do this when working with async tasks?

private const int TotalNumberOfAttempts = 10;

public static async Task<HttpResponseMessage> PutWithRetriesAsync(string url,
HttpContent content,
{
var numberOfAttempts = 0;
ExceptionDispatchInfo capturedException;

do
{
try
{
}
catch (AggregateException ex)
{
capturedException = ExceptionDispatchInfo.Capture(ex);
numberOfAttempts++;
}
} while (numberOfAttempts < TotalNumberOfAttempts);

if (capturedException != null)
{
capturedException.Throw();
}
throw new Exception("That will never be thrown");
}


I would rewrite your code into a while (true) loop, that can only be exited using the return in your try or using a throw; inside a condition in your catch:

while (true)
{
try
{
}
catch
{
numberOfAttempts++;
if (numberOfAttempts >= TotalNumberOfAttempts)
throw;
}
}


This way, you don't need ExceptionDispatchInfo or the useless (but required by the compiler) throw at the end.

I also changed catch (AggregateException ex) to catch all exceptions, because await usually doesn't throw AggregateException (unlike task.Wait() or task.Result).

• This is a good approach. I had originally done the same thing with a do, but without the ExceptionDispatchInfo. I was just under the impression that the EDI was the right way to deal with async exceptions. – Chase Florell Aug 13 '14 at 15:19
• @ChaseFlorell If you need to catch it in one place and then rethrow it somewhere else, yes. But you don't need to do that here, throw; will work as well and it's simpler. – svick Aug 13 '14 at 15:20
• ah, makes sense. So this approach is equally as valid. gist.github.com/ChaseFlorell/9524b2e8a85dfa5b563e – Chase Florell Aug 13 '14 at 15:30
• @ChaseFlorell Not really, throw ex; on an exception that you caught will overwrite the stack trace of the exception. The entire point of EDI is to avoid that. So, you should use just throw; when you can. And when you can't, use EDI, but don't rethrow an exception using throw ex;. – svick Aug 13 '14 at 15:40
• makes perfect sense. – Chase Florell Aug 13 '14 at 16:15

private const int TotalNumberOfAttempts = 10;

public static async Task<HttpResponseMessage> PutWithRetriesAsync(string url,
HttpContent content,
{
var exceptions = new List<Exception>();
for(int i = 0; i < TotalNumberOfAttempts; i++)
{
try
{
}
catch (AggregateException ex)
{

You can easily abstract this functionality into an extension method to provide retry logic for any arbitrary Func<Task>.