# Retrying a RestRequest

I wrote a method that lets me retry RestSharp requests when a specific exception is thrown.
Here's the code for that method:

private async Task<T_Out> RunRequestWithRetry<T_In, T_Out>(Func<SignatureAccount, T_In, T_Out> request, SignatureAccount signatureAccount, T_In requestBody) where T_Out : Task
{
try
{
var result = request.Invoke(signatureAccount, requestBody) as Task;
await result;
return result as T_Out;
}
catch (AMAApiException<ErrorResult> apiEx)
{
if (apiEx.ErrorType != AMAErrorType.TokenNeedsRefresh)
throw apiEx;

// Refresh Tokens
var signatureAccountService = new SignatureAccountService(MvcApplication.Config);
var newSignatureAccount = await signatureAccountService.UpdateTokens(signatureAccount);
if (newSignatureAccount == null)
throw apiEx;

StorageService.SignatureAccount.SaveState(newSignatureAccount);
return request.Invoke(newSignatureAccount, requestBody);
}
}


This works well enough since all the requests that'll be passing through this method have the same signature and all of them will return a Task.

My only issue with this code is that I need to call await 2 when I call it, for example:

var credentialsListResponse =  await await RunRequestWithRetry(GetCredentialsList, process.SignatureAccount, credentialsListBody);


I tried to return only the T_Out since I specified it to be of type Task but visual studio does not like that... So I had to wrap the return with a Task, but this makes my method return Task<Task<Type>>.

Is there a way to make this more elegant, maybe remove one of the await calls.

• BTW You can call the Unwrap, (1) to flatten Task<Task<T>> to Task<T> Dec 6 '21 at 8:14
• A cool method to know, thanks! Dec 6 '21 at 8:27

Here are few suggestions :

First, make a loop

• the readability is improved as you will see only request.Invoke(signatureAccount, requestBody) call in one place.
• you will get rid of the nested Task
• it will be more simple if your customer decide to ask 3 retries or more.

Second, RunRequestWithRetry is fullfilling too much roles. It should focus only on retrying a request as its name suggest. Refreshing tokens have no place here. A trick could be passing a "CleanupOnException" task as argument that the caller should provide.

Finally, if you can use some nuget packages, there is a very serious and popular one (top 7 non-microsoft nuget packages) which do exactly what you want in an elegant manner: Polly.

• Thank you for your sugestion, I dont really require do define a number of retries, I only need to retry once in case the token has expired, thats why the code to refresh the token is there, but I could it refresh the tokens in a separate method, anyways I managed to find a solution for the problem I was having. Dec 2 '21 at 15:57

I found out what was wrong with my logic, it was quite a simple fix that solves the double await problem, I just needed to make the signature of the method Func<SignatureAccount, T_In, Task<T_Out>> this way the T_Out will be the correct type and I can return Task<T_Out>.

I also took the advise by @Perfect28 and extracted the refreshToken logic to a new method called UpdateTokensAndRetry, this works for me since I only need to retry once after refreshing the tokens, since I only want to refresh when the tokens exipe.

Here's the code sample with the solution:

private async Task<T_Out> RunRequestWithRetry<T_In, T_Out>(Func<SignatureAccount, T_In, Task<T_Out>> request, SignatureAccount signatureAccount, T_In requestBody)
{
try
{
return await request.Invoke(signatureAccount, requestBody);
}
catch (AMAApiException<ErrorResult> apiEx)
{
if (apiEx.ErrorType != AMAErrorType.TokenNeedsRefresh)
throw apiEx;

return await UpdateTokensAndRetry(request, signatureAccount, requestBody, apiEx);
}
}

private async Task<T_Out> UpdateTokensAndRetry<T_In, T_Out, T_Ex>(Func<SignatureAccount, T_In, Task<T_Out>> request, SignatureAccount signatureAccount, T_In requestBody, T_Ex apiEx) where T_Ex : Exception
{
// Refresh Tokens
var signatureAccountService = new SignatureAccountService(MvcApplication.Config);
var newSignatureAccount = await signatureAccountService.UpdateTokens(signatureAccount);
if (newSignatureAccount == null)
throw apiEx;


var signHashesResponse = await RunRequestWithRetry(GetSignedHashes, process.SignatureAccount, signHashesRequest);