Как использовать ключевое слово await внутри метода без изменения асинхронного метода

Я разрабатываю запланированное задание для отправки сообщения в очередь сообщений с помощью Quartz.net. Метод Execute IJob не является асинхронным. поэтому я не могу использовать асинхронную задачу. Но я хочу вызвать метод с ключевым словом await.

Пожалуйста, найдите ниже мой код. Не уверен, правильно ли я поступаю. Кто-нибудь может помочь мне с этим?

private async Task PublishToQueue(ChangeDetected changeDetected)
{
    _logProvider.Info("Publish to Queue started");

    try
    {
       await _busControl.Publish(changeDetected);

        _logProvider.Info($"ChangeDetected message published to RabbitMq. Message");
    }
    catch (Exception ex)
    {
        _logProvider.Error("Error publishing message to queue: ", ex);

        throw;
    }
}

public class ChangedNotificatonJob : IJob
{
    public void Execute(IJobExecutionContext context)
    {
                    //Publish message to queue
                    Policy
                        .Handle<Exception>()
                        .RetryAsync(3, (exception, count) =>
                        {
                            //Do something for each retry
                        })
                        .ExecuteAsync(async () =>
                        {
                            await PublishToQueue(message);
                        });
    }
}

Это правильный путь? Я использовал .GetAwaiter();

Policy
        .Handle<Exception>()
        .RetryAsync(_configReader.RetryLimit, (exception, count) =>
        {
            //Do something for each retry
        })
        .ExecuteAsync(async () =>
        {
            await PublishToQueue(message);
        }).GetAwaiter()

person Mukil Deepthi    schedule 05.09.2016    source источник
comment
Возможный дубликат Как вызвать асинхронный метод из синхронного метода в С#?   -  person smoksnes    schedule 06.09.2016


Ответы (1)


Полли .ExecuteAsync() возвращает Task. С любым Task вы можете просто вызвать для него .Wait() (или другие методы блокировки) для синхронной блокировки, пока он не завершится или не выдаст исключение.

Как вы уже заметили, поскольку IJob.Execute(...) не является async, вы не можете использовать await, поэтому у вас нет другого выбора, кроме как синхронно заблокировать задачу, если вы хотите определить успех публикации до того, как IJob.Execute(...) вернется.

.Wait() вызовет повторное создание любого исключения из задачи, завернутого в AggregateException. Это произойдет, если все повторные попытки, организованные Polly, завершатся ошибкой.

Вам нужно решить, что делать с этим исключением:

  • Если вы хотите, чтобы вызывающий объект обработал его, перебросьте его или не перехватывайте и позвольте ему каскадироваться вне задания Quartz.

  • Если вы хотите обработать его перед возвращением из IJob.Execute(...), вам понадобится try {} catch {} вокруг всего .ExecuteAsync(...).Wait(). Или рассмотрим синтаксис Polly .ExecuteAndCaptureAsync(...): он избавляет вас от необходимости предоставлять этот внешний try-catch, вместо этого помещая окончательный результат выполнения в экземпляр PolicyResult. См. документацию Polly.


Существует еще одна альтернатива, если ваше единственное намерение состоит в том, чтобы зарегистрировать где-нибудь, что публикация сообщения не удалась, и вам все равно, произойдет ли это ведение журнала до возврата IJob.Execute(...) или нет. В этом случае вместо использования .Wait() вы можете привязать задачу продолжения к ExecuteAsync(), используя .ContinueWith(...), и обрабатывать там любые журналы. Мы применяем этот подход и фиксируем неудачную публикацию сообщения в специальной «больнице сообщений», собирая достаточно информации, чтобы мы могли выбрать, следует ли повторно опубликовать это сообщение позже, если это необходимо. Ценность этого подхода зависит от того, насколько важно для вас никогда не потерять сообщение.


РЕДАКТИРОВАТЬ: GetAwaiter() не имеет значения. Это не позволит вам волшебным образом начать использовать await внутри метода, отличного от async.

person mountain traveller    schedule 05.09.2016
comment
Вы также можете увидеть это: quartz- scheduler.net/2016/08/16/ . Quartz.Net планирует добавить поддержку async/await в выпуске v3 (в настоящее время в предварительной версии на NuGet). Предположительно, это должно дать вам интерфейс IJobAsync или IAsyncJob или аналогичный. Хотя это не задокументировано на момент написания. - person mountain traveller; 05.09.2016
comment
GetAwaiter().GetResult() — лучшая замена Wait(), поскольку она позволяет избежать оболочки AggregateException. - person Stephen Cleary; 06.09.2016
comment
Я хотел бы услышать отзывы о том, как Quartz.NET 3.0 alpha 2 будет работать с вами. Он имеет поддержку задач async/await, которую вы ищете. В руководстве по миграции задокументировано большинство различий . - person Marko Lahma; 06.09.2016