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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With