Proper way to asynchronously send an email in ASP.

2019-01-30 16:23发布

When a user registers on my website, I don't see why I need to make him "wait" for the smtp to go through so that he gets an activation email.

I decided I want to launch this code asynchronously, and it's been an adventure.

Lets imagine I have a method, such as:

private void SendTheMail() { // Stuff }

My first though.. was threading. I did this:

Emailer mailer = new Emailer();
Thread emailThread = new Thread(() => mailer.SendTheMail());
emailThread.Start();

This works... until I decided to test it for error-handling capability. I purposely broke the SMTP server address in my web.config and tried it. The scary result was that IIS basically BARFED with an unhandled exception error on w3wp.exe (it was a windows error! how extreme...) ELMAH (my error logger) did NOT catch it AND IIS was restarted so anyone on the website had their session erased. Completely unacceptable result!

My next thought, was to do some research on Asynchronous delegates. This seems to work better because exceptions are being handled within the asynch delegate (unlike the thread example above). However, i'm concerned if i'm doing it wrong or maybe I'm causing memory leaks.

Here's what i'm doing:

Emailer mailer = new Emailer();
AsyncMethodCaller caller = new AsyncMethodCaller(mailer.SendMailInSeperateThread);
caller.BeginInvoke(message, email.EmailId, null, null);
// Never EndInvoke... 

Am I doing this right?

9条回答
趁早两清
2楼-- · 2019-01-30 16:55

As of .NET 4.5 SmtpClient implements async awaitable method SendMailAsync. As a result, to send email asynchronously is as the following:

public async Task SendEmail(string toEmailAddress, string emailSubject, string emailMessage)
{
    var message = new MailMessage();
    message.To.Add(toEmailAddress);

    message.Subject = emailSubject;
    message.Body = emailMessage;

    using (var smtpClient = new SmtpClient())
    {
        await smtpClient.SendMailAsync(message);
    }
} 
查看更多
手持菜刀,她持情操
3楼-- · 2019-01-30 16:55

Use this way-

private void email(object parameters)
    {
        Array arrayParameters = new object[2];
        arrayParameters = (Array)parameters;
        string Email = (string)arrayParameters.GetValue(0);
        string subjectEmail = (string)arrayParameters.GetValue(1);
        if (Email != "Email@email.com")
        {
            OnlineSearch OnlineResult = new OnlineSearch();
            try
            {
                StringBuilder str = new StringBuilder();
                MailMessage mailMessage = new MailMessage();

                //here we set the address
                mailMessage.From = fromAddress;
                mailMessage.To.Add(Email);//here you can add multiple emailid
                mailMessage.Subject = "";
                //here we set add bcc address
                //mailMessage.Bcc.Add(new MailAddress("bcc@site.com"));
                str.Append("<html>");
                str.Append("<body>");
                str.Append("<table width=720 border=0 align=left cellpadding=0 cellspacing=5>");

                str.Append("</table>");
                str.Append("</body>");
                str.Append("</html>");
                //To determine email body is html or not
                mailMessage.IsBodyHtml = true;
                mailMessage.Body = str.ToString();
                //file attachment for this e-mail message.
                Attachment attach = new Attachment();
                mailMessage.Attachments.Add(attach);
                mailClient.Send(mailMessage);
            }

    }


  protected void btnEmail_Click(object sender, ImageClickEventArgs e)
    {
        try
        {
            string To = txtEmailTo.Text.Trim();
            string[] parameters = new string[2];
            parameters[0] = To;
            parameters[1] = PropCase(ViewState["StockStatusSub"].ToString());
            Thread SendingThreads = new Thread(email);
            SendingThreads.Start(parameters);
            lblEmail.Visible = true;
            lblEmail.Text = "Email Send Successfully ";
        }
查看更多
Deceive 欺骗
4楼-- · 2019-01-30 16:58

So, why not have a separate poller/service which deals exclusively with sending emails? Thus, allowing your registration post-back to execute in only the time it takes to write to the database/message queue and delaying the sending of the email til the next polling interval.

I'm pondering the same issue just now and I'm thinking that I really don't want to even initiate the email sending within the server post back request. The process behind serving the web pages should be interested in getting a response back to the user ASAP, the more work you try to do the slower it will be.

Have a look at the Command Query Segregation Principal (http://martinfowler.com/bliki/CQRS.html). Martin Fowler explains that different models can be used in the command part of an operation than are used in the query part. In this scenario the command would be "register user", the query would be the activation email, using the loose analogy. The pertinent quote would probably be:

By separate models we most commonly mean different object models, probably running in different logical processes

Also worth a read is the Wikipedia article on CQRS (http://en.wikipedia.org/wiki/Command%E2%80%93query_separation). An important point which this highlights is:

it is clearly intended as a programming guideline rather than a rule for good coding

Meaning, use it where your code, program execution and programmer understanding would benefit. This being a good example scenario.

This approach has the added benefit of negating all the mufti-threading concerns and the headaches all that can bring.

查看更多
登录 后发表回答