2

I read the documentation for async-await feature, but still highly confused on how to structure async-await code that calls SmtpClient.SendMailAsync() method.
How should I rewrite the code to properly use async-await ?

Currently, my code looks like this:
Periodically PrivateSignal is being called by the system to send out email notifications.

public override void PrivateSignal(IEventInformation ev) 
{
    TagEvent tag = (TagEvent)ev;
    Task.Run(async () =>
    {
        try
        {
           await smptClient.SendMailAsync(CaptureRC.SmptFromEmailAddr,
                                    ToEmails,
                                    CaptureRC.EmailSubject,
                                    "seen moving" + tag.ToString());
        }
        catch (AggregateException ex)
        {
           //TODO handle the error
           //TODO log the erros, along with
        }
    }).Wait();
}
i3arnon
  • 113,022
  • 33
  • 324
  • 344
newprint
  • 6,936
  • 13
  • 67
  • 109

2 Answers2

4

You should be making PrivateSignal async as well which includes marking it with async and returning a Task:

public override async Task PrivateSignalAsync(IEventInformation ev) 
{
    TagEvent tag = (TagEvent)ev;
    try
    {
       await smptClient.SendMailAsync(CaptureRC.SmptFromEmailAddr, ToEmails, CaptureRC.EmailSubject, "seen moving" + tag.ToString());
    }
    catch (Exception ex)
    {
        // ...
    }
}

It seems, though, that you can't do that since PrivateSignal is overriding an already synchronous method. If you can change the base method to return a Task then you should, but if you can't then simply don't use async at all inside that method because blocking on async can lead to deadlocks and other unwanted results:

public override void PrivateSignal(IEventInformation ev) 
{
    TagEvent tag = (TagEvent)ev;
    try
    {
       smptClient.SendMail(CaptureRC.SmptFromEmailAddr, ToEmails, CaptureRC.EmailSubject, "seen moving" + tag.ToString());
    }
    catch (Exception ex)
    {
        // ...
    }
}
i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • 1
    smptClient.SendMail() shouldn't throw an AggregateException. AggregateException is something that TPL (Task, Task, etc) methods throw. – Tim Goyer Feb 10 '15 at 22:30
  • Lets assume that I can change change the code to be asynchronous, same way as you have it. 1. How do I call this method from other function ? `await PrivateSignal(tag)` or `Task.Run(() => PrivateSignal(tag)).Await()` ? 2. Should the calling method be `async` as well ? – newprint Feb 10 '15 at 23:57
  • 1
    @newprint `await PrivateSignalAsync(tag)`. `Task.Run` should only be used to offload compute-bound work to a `ThreadPool` thread. – i3arnon Feb 10 '15 at 23:59
  • Another small question, you mentioned "blocking on `async` can lead to deadlocks and other unwanted results" . However `.Wait()` is blocking a thread on which `PrivateSignal` is running on, why it can cause deadlocks ? – newprint Feb 11 '15 at 03:06
  • 1
    @newprint [await vs Task.Wait - Deadlock?](http://stackoverflow.com/a/13140963/885318) – i3arnon Feb 11 '15 at 07:42
1

async/await is the new hotness to replace things like Task.Run and Task.Wait. Try this:

public async override Task PrivateSignal(IEventInformation ev) 
{
    TagEvent tag = (TagEvent)ev;

    try
    {
        await smptClient.SendMailAsync(CaptureRC.SmptFromEmailAddr,
            ToEmails, CaptureRC.EmailSubject, "seen moving" + tag.ToString());
    }
    catch (Exception ex)
    {
        //TODO handle the error
        //TODO log the erros, along with
    }
}

The await causes the running thread to suspend until the Task completes (or is cancelled or experiences an error). The rest of the method is registered as a continuation automatically. async/await is therefore syntactic sugar for task-based asynchronous programming.

Also, it's important to avoid async void methods since the void support is reserved for event handlers. Instead of void, return Task for methods with no return value.

Finally, since this method is an override, you'd need to change the base method's return type to Task as well.

Haney
  • 32,775
  • 8
  • 59
  • 68