0

I am developing a scheduled job to send message to Message queue using Quartz.net. The Execute method of IJob is not async. so I can't use async Task. But I want to call a method with await keyword.

Please find below my code. Not sure whether I am doing correct. Can anyone please help me with this?

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);
                        });
    }
}

Is this correct way? I have used .GetAwaiter();

Policy
        .Handle<Exception>()
        .RetryAsync(_configReader.RetryLimit, (exception, count) =>
        {
            //Do something for each retry
        })
        .ExecuteAsync(async () =>
        {
            await PublishToQueue(message);
        }).GetAwaiter()
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Mukil Deepthi
  • 6,072
  • 13
  • 71
  • 156
  • Possible duplicate of [How to call asynchronous method from synchronous method in C#?](http://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c) – smoksnes Sep 06 '16 at 05:14

1 Answers1

1

Polly's .ExecuteAsync() returns a Task. With any Task, you can just call .Wait() on it (or other blocking methods) to block synchronously until it completes, or throws an exception.

As you have observed, since IJob.Execute(...) isn't async, you can't use await, so you have no choice but to block synchronously on the task, if you want to discover the success-or-otherwise of publishing before IJob.Execute(...) returns.

.Wait() will cause any exception from the task to be rethrown, wrapped in an AggregateException. This will occur if all Polly-orchestrated retries fail.

You'll need to decide what to do with that exception:

  • If you want the caller to handle it, rethrow it or don't catch it and let it cascade outside the Quartz job.

  • If you want to handle it before returning from IJob.Execute(...), you'll need a try {} catch {} around the whole .ExecuteAsync(...).Wait(). Or consider Polly's .ExecuteAndCaptureAsync(...) syntax: it avoids you having to provide that outer try-catch, by instead placing the final outcome of the execution into a PolicyResult instance. See the Polly doco.


There is a further alternative if your only intention is to log somewhere that message publishing failed, and you don't care whether that logging happens before IJob.Execute(...) returns or not. In that case, instead of using .Wait(), you could chain a continuation task on to ExecuteAsync() using .ContinueWith(...), and handle any logging in there. We adopt this approach, and capture failed message publishing to a special 'message hospital' - capturing enough information so that we can choose whether to republish that message again later, if appropriate. Whether this approach is valuable depends on how important it is to you never to lose a message.


EDIT: GetAwaiter() is irrelevant. It won't magically let you start using await inside a non-async method.

mountain traveller
  • 7,591
  • 33
  • 38
  • You might also want to see this: http://www.quartz-scheduler.net/2016/08/16/quartznet-3.0-alpha1-released.html . Quartz.Net plan to add `async`/`await` support in the v3 release (currently in preview on NuGet). Presumably that should give you an `IJobAsync` or `IAsyncJob` interface or similar. Tho it's not [documentated](http://quartznet.sourceforge.net/apidoc/3.0/html/) at time of writing. – mountain traveller Sep 05 '16 at 18:08
  • 1
    `GetAwaiter().GetResult()` is a nicer replacement for `Wait()`, since it avoids the `AggregateException` wrapper. – Stephen Cleary Sep 06 '16 at 02:13
  • I'd love to hear feedback how Quartz.NET 3.0 alpha 2 would fare in your use. It has the async/await Task support you are looking for. [Migration guide](http://www.quartz-scheduler.net/documentation/quartz-3.x/migration-guide.html) has most of the differences documented. – Marko Lahma Sep 06 '16 at 17:31