Wai Yan Hein Wai Yan Hein - 9 days ago 5
ASP.NET (C#) Question

Sending background email asynchronous still freezing the UI

I am developing an ASP.NET MVC project. I am trying to send email in background running without freezing UI. Most of the articles say now it can be done with .NET asynchronous sending email. I am sending asynchronous email but it is still freezing the UI. What I am doing is I register entering email and password then send verification email in the background. Please see my sending email code below.

I have interface like this.

public interface IMailHelper
{
Task SendEmailAsync(string to, string subject, string body, bool isHtml = true);
}


This is the interface implementation with send-email method

public class MailHelper : IMailHelper
{
private string EmailAddress { get; set; }
private string EmailPassword { get; set; }
private int Port { get; set; }
private string Host { get; set; }

public MailHelper()
{
this.EmailAddress = AppConfig.SystemEmailAddress;
this.EmailPassword = AppConfig.SystemEmailPassword;
this.Port = AppConfig.SystemMailPort;
this.Host = AppConfig.SystemMailHost;
}

public async Task SendEmailAsync(string to, string subject, string body, bool isHtml = true)
{
var mail = new MailMessage();
mail.To.Add(to);
mail.From = new MailAddress(this.EmailAddress);
mail.Subject = subject;
mail.Body = body;
mail.IsBodyHtml = isHtml;

using (var smtp = new SmtpClient())
{
var credential = new NetworkCredential
{
UserName = this.EmailAddress,
Password = this.EmailPassword
};
smtp.UseDefaultCredentials = false;
smtp.Credentials = credential;
smtp.Host = this.Host;
smtp.Port = this.Port;
smtp.EnableSsl = true;
await smtp.SendMailAsync(mail);
}
}
}


As you can see I am sending asynchronous email.

This is my action method in controller

[HttpPost]
[AllowAnonymous]
//[AngularValidateAntiForgeryToken]
public async Task<JsonResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
//Do other stuffs here
await _mailHelper.SendEmailAsync(user.Email, AppConfig.AccountVerifyMailSubject, WebHelper.BuildAccountVerificationEmailBody(user.UserName, verificationUrl));
}
// Do other stuffs here
}


As you can see I am trying to send email in background in the action method. My code is sending email successfully. But I am sure it is freezing the UI cause it take longer than it should be. I tested many times. Not different with sending email normally. Please why it is freezing the UI? My code not sending background email in new process? Please what is wrong with my code how can I correct my code? Please help me. Or is my code correct?

Answer

Most of the articles say now it can be done with .NET asynchronous sending email.

Those articles are all wrong, sorry.

I have a blog post that goes into various implementations of ASP.NET background tasks. In summary, if you don't care that emails are occasionally lost, then you can just use HostingEnvironment.QueueBackgroundWorkItem:

[HttpPost]
[AllowAnonymous]
public async Task<JsonResult> Register(RegisterViewModel model)
{
  if (ModelState.IsValid)
  {
    ...
    HostingEnvironment.QueueUserWorkItem(_ =>
        _mailHelper.SendEmailAsync(user.Email, AppConfig.AccountVerifyMailSubject, WebHelper.BuildAccountVerificationEmailBody(user.UserName, verificationUrl));
  }
  ...
}

If you do care that emails are not lost, then you'll have to place them in some sort of reliable queue (Azure Queue, or MSMQ) and have an independent background worker send the actual email (Azure Function / WebJob / Worker Role, or Win32 service).