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 DelegatingHandler
s using IHttpClientFactory.
Wait
. Try making the function async and awaiting inside – Hexamethylenetetraminetask
local var, which is not used by your policy. Polly is calling func on its own. – Hexamethylenetetraminepublic static T Execute<T>(Func<T> func) where T : Task
– Trapper