ASP.Net – Sending Email Asynchronously

I’ve spent a lot of time over the last few months building a website, and as always, there is much help and information available on the internet. Something I struggled with however was the sending of emails… How can I send an email from my site without blocking the calling process? “Use sendAsync!” I hear people shouting. Having tried this I can say that it doesn’t help where asp.net is concerned. I implemented it myself, and whilst it is almost seamless on a web server with fast connectivity to the smtp box, what I found is there are occasions where this just isn’t fast enough, and the user experience suffers. Here’s what I have done to resolve this… I hope it saves someone the head scratching that I went through 😉

An example on the site I have built, is a simple notification to let a user know that they have received a message. The sender writes their message on a modal box, and clicks the send button. What was occurring on my development website (slow connection to the smtp relay) was that the page would freeze, and user would be left wondering what was happening (perhaps they would click the send button some more) until after approximately 10 seconds, the page would get it’s SendCompleted callback and the page would return the updated response. Unfortunately for my purposes, I needed a way that I could fire and forget, whilst still tracking the status.

My solution was to invoke a delegated sendAsync, with a callback function which updates the database. I wasn’t able to find any references to this solution anywhere on the web, so I thought I’d share it here.

The wrapper for sending mail
This will always be called after the message record is inserted into the database, so we can pass in the identity as the message identifier.

protected Boolean SendMail(string Subject, string Body, Int64 MessageIdentifier)
        {
                //create the mail message
                MailMessage MyEmail = new MailMessage();

                //set the addresses
                MyEmail.From = new MailAddress("me@example.com");
                MyEmail.To.Add("you@example.com");

                //set the content
                MyEmail.Subject = Subject;
                MyEmail.Body = Body;
                MyEmail.IsBodyHtml = true;

                //Preparing the client
                SmtpClient MyMailClient = new SmtpClient("smtp.example.com");

                MyMailClient.Credentials = new System.Net.NetworkCredential("SmtpUserName", "password");

                // Create an eventhandler to update the message record when email sending complete
                MyMailClient.SendCompleted += (sender, e) =>
                {
                    //do something when the send is done.
                    SendCompletedCallback(sender, e);
                    MyEmail.Dispose();
                };

                // Delegate this task
                SendEmailDelegate  MyMailDelegate = new SendEmailDelegate(MyMailClient.SendAsync);

                // When the task completes, end invoke
                AsyncCallback MyDelegateCallback = new AsyncCallback(DelegatedAsyncCallback);

                // Invoke the task with necessary parameters
                MyMailDelegate.BeginInvoke(MyEmail, MessageIdentifier, MyDelegateCallback, MyMailDelegate);

                return true;
                }
            catch (SmtpException smtpEx)
            {
                WriteToLog(smtpEx.TargetSite.ToString(), "Could not send email: " + smtpEx.Message + "| inner exception: " + smtpEx.InnerException);
                return false;
            }
            catch (Exception ex)
            {
                WriteToLog(ex.TargetSite.ToString(), ex.Message);
                return false;
            }

Create a delegate

        //Delegated sendAsync method
        private delegate void SendEmailDelegate(MailMessage thisMessage, object thisMessageID);

This method will be called when the delegated task is finished

        //Task completed - end invokation
        protected void DelegatedAsyncCallback(IAsyncResult AsyncResult)
        {
            SendEmailDelegate MySendMailDelegate = (SendEmailDelegate)(AsyncResult.AsyncState);

            MySendMailDelegate.EndInvoke(AsyncResult);
        }

This method is called when the sendAsync has complete
We can use it to update a log/database to record the outcome.

        // The SmtpClient.sendAsync callback
        protected void SendCompletedCallback(object sender, System.ComponentModel.AsyncCompletedEventArgs e )
        {
            // Get the value that async was invoked with for the message
            Int64 MessageID = Convert.ToInt64(e.UserState);

            {
                try
                {
                //Update the message record to show that notification was sent to recipient

                /* YOUR DB UPDATE CODE GOES HERE */

                e.g usp_MailNotification_Update @MessageIdentifier = MessageID (This is pseudo code - please don't try to use this)
                }
                catch (Exception Ex)
                {
                    WriteToLog("Failure updating notification status of message: " + Ex.TargetSite.ToString(), Ex.Message);
                }
            }
        }

If there are any C#/.Net gurus out there who can help me improve this code, I’d be greatful as the threading/memory impacts of this approach are not completely clear to me! But I have been testing this and so far so good.

Leave a Reply

Your email address will not be published.