Для вспомогательного метода для асинхронных выполнений с помощью жестко заданной политики Полли, где выполнения асинхронно возвращают тип TResult
, через Task<TResult>
вы можете принять:
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());
}
(учитываяиспользуемая политика каждый раз одна и та же, вы можете также сохранить политику в статическом поле и создать ее только один раз.)
Примечание: Это (намеренно) не соответствуетконтракт, указанный в вашем комментарии к вашему первоначальному вопросу, как неразрывный:
public static T Execute<T>(Func<T> func) where T : Task
Предложение where T : Task
изначально выглядит привлекательным для асинхронного или асинхронного - типа метода, предназначенного для работы слибо Task
, либо Task<T>
.Джон Скит объясняет здесь и здесь , почему он не работает с асинхронной синхронизацией.Предложенная вами сигнатура вспомогательного метода сама по себе не является асинхронной:
public static T Execute<T>(Func<T> func) where T : Task
Однако, введение .ExecuteAsync(async () => await func());
в вашем примере кода вызывает аналогичную проблему.Перегрузка Polly .ExecuteAsync(...)
именно для того, чтобы хорошо играть с async
/ await
, существует в двух основных формах:
(1) Task ExecuteAsync(Func<Task> func)
(2) Task<TResult> ExecuteAsync<TResult>(Func<Task<TResult>> func)
Компилятор должен выбрать один или другой во время компиляции : он не может (в вашем примере кода) скомпилировать его, чтобы он был либо (1) или (2) во время выполнения .Поскольку он знает только T : Task
, он выбирает (1), что возвращает Task
.Отсюда и ошибка, которую вы видите в своем комментарии к ответу @ JamesFaix: Cannot implicitly convert type Task to T
.
Если вам нужны вспомогательные шаблоны этой формы, которые вызывающие абоненты могут использовать для Task
или Task<TResult>
- повторных вызовов, выпридется объявить оба:
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);
}
}
Наконец, если это для упаковки вызовов, размещенных через HttpClient
, рекомендуемый шаблон - поместить политики Полли в DelegatingHandler
, как описано в @Ответ Мухаммеда Рехана Сайда здесь .ASP.NET Core 2.1 поддерживает краткое объявление для создания таких DelegatingHandler
s с использованием IHttpClientFactory .