Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebApi Controller - Send Mail Async

I have a WebApi Controller that one of the parts is sending emails to a set of users.

[HttpPost]
[Authorize]
[Route("{id}/Do")]
public async Task<HttpResponseMessage> Post(int id, Model model)
...
    await _emailService.SendAsync(message);
...

Now the method that sends the emails (SendGrid)

public override async Task SendAsync(MailMessage message)
{
    var client =
      new SmtpClient(SendGridServerName, SendGridServerPort)
      {
          Port = SendGridServerPort,
          DeliveryMethod = SmtpDeliveryMethod.Network,
          UseDefaultCredentials = false
      };

    var credentials = new NetworkCredential(SendGridUserName, SendGridPassword);

    client.EnableSsl = true;
    client.Credentials = credentials;
    message.From = new MailAddress(FromAddress);

    await client.SendMailAsync(message);
}

Everything works, but it's very slow. I was expecting it to be fast, but the await _emailService.SendAsync(message); does not appear to be async. It stops there for a while.

Any ideas?

Thanks

like image 560
user2779312 Avatar asked May 16 '15 14:05

user2779312


1 Answers

What async does is allowing your server to run other threads while your slow method is executed asynchronously. I.e., when you await for an async method, the server executes a different thread until the async method finishes execution, and then keep running the thread that called the async method. Async actions in a controller are treated exactly in the same way behind the scenes. So, the response from your async action to the browser will not happen until the async email sending has finished.

NOTE: for example, if there are connectivity problems to the email server, or the DNS resolution, you'll usually get a time out after 30 seconds, so your thread will be slept during 30 seconds, and only then will send the answer to the browser

If you want to return the response to your browser quickly, you need to implement a different idea, which is to start a new thread that sends the email, but don't wait for it to finish, so that your thread keeps running and inmediately returns the asnwer to the browser. That's known as fire and forget. To understand what I'm speaking about, please see this: Fire and forget approach. And then read this other carefully: Fire and Forget (Asynch) ASP.NET Method Call. Take into account that MVC itself is threaded and you need to have it into account when using the fire and forget approach.

Obvioulsy in fire and forget, the controller will not be able to detect errors during the email sending, beacause the new thread runs on its own while the main thread has already finished. So you have to implement something to at least log the possible error, and ideally let the user now what happened (for example which reports that he can see later on). Please, see this: ASP.NET Exception Handling in background threads

like image 99
JotaBe Avatar answered Nov 14 '22 21:11

JotaBe