For a helper method for async executions through a hard-coded Polly policy, where the executions asynchronously return a type TResult, via a Task<TResult>, you could adopt:
public static Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> func)
{
return Policy.Handle<HttpRequestException>()
.Or<TimeoutException>()
.WaitAndRetryAsync(
3,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(5, retryAttempt)),
(exception, timeSpan, retryCount, context) =>
{
//do some logging
})
.ExecuteAsync<TResult>(func); // This is an async-await-eliding contraction of: .ExecuteAsync<TResult>(async () => await func());
}
(Given the policy used is the same every time, you could consider also storing the policy in a static field and creating it only once.)
Note: This (intentionally) doesn't conform to the contract stated in your comment to your original question, as being unbreakable:
public static T Execute<T>(Func<T> func) where T : Task
The clause where T : Task initially looks appealing for an or async async-like method designed to work with either Task or Task<T>. Jon Skeet explains here and here why it doesn't work with async. Your proposed helper method signature is not in-itself async:
public static T Execute<T>(Func<T> func) where T : Task
However, introducing .ExecuteAsync(async () => await func()); in your example code forces a similar issue. The Polly .ExecuteAsync(...) overload, precisely in order to play nicely with async/await, exists in two main forms:
(1) Task ExecuteAsync(Func<Task> func)
(2) Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> func)
The compiler has to select one or the other at compile time: it can't (in your example code) compile it to be either (1) or (2) at runtime. Since it only knows T : Task it selects (1), which returns Task. Hence the error you see in your comment to @JamesFaix's answer: Cannot implicitly convert type Task to T.
If you want helper patterns of this form, which callers can use for either Task or Task<TResult> -returning calls, you would have to declare both:
class HttpRetryWrapper
{
private static policy = Policy.Handle<HttpRequestException>()
.Or<TimeoutException>()
.WaitAndRetryAsync(
3,
retryAttempt => TimeSpan.FromSeconds(Math.Pow(5, retryAttempt)),
(exception, timeSpan, retryCount, context) =>
{
//do some logging
});
public static Task ExecuteAsync(Func<Task> func)
{
return policy.ExecuteAsync(func);
}
public static Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> func)
{
return policy.ExecuteAsync<TResult>(func);
}
}
Finally, if this is for wrapping calls placed through HttpClient, the recommended pattern is to place the Polly policies in a DelegatingHandler as described in @MuhammedRehanSaeed's answer here. ASP.NET Core 2.1 supports concise declaration for creating such DelegatingHandlers using IHttpClientFactory.