perkes456
perkes456

Reputation: 1181

Sending Email asynchronously in ASP.NET C#

I was wondering if theres a way to send email asynchronously in asp.net c#. I've used the following code to send the emails:

 if (checkObavijest.Checked)
            {
                List<hsp_Kupci_Newsletter_Result> lista = ServisnaKlasa.KupciNewsletter();
                for (int i = 0; i < lista.Count; i++)
                {
                    MailMessage mail = new MailMessage();

                    mail.From = new MailAddress("*******");
                    mail.To.Add(lista[i].Email);
                    mail.Subject = "Subject";
                    mail.SubjectEncoding = Encoding.UTF8;
                    mail.BodyEncoding = Encoding.UTF8;
                    mail.IsBodyHtml = true;
                    mail.Priority = MailPriority.High;
                    mail.Body = "Some message";
                    SmtpClient smtpClient = new SmtpClient();
                    Object state = mail;
                    smtpClient.Credentials = new NetworkCredential("****@gmail.com", "****");
                    smtpClient.Port = 587;
                    smtpClient.Host = "smtp.gmail.com";
                    smtpClient.EnableSsl = true;
                    smtpClient.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
                    try
                    {
                        smtpClient.SendAsync(mail, state);
                    }
                    catch (Exception ex)
                    {

                    }
                }

 void smtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {

            MailMessage mail = e.UserState as MailMessage;

            if (!e.Cancelled && e.Error != null)
            {
                string poruka = "Emailovi poslani!";
                ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + poruka + "');", true);
            }
        }

But when I tick the option for sending emails, the page loads like 40 seconds, then after 40 seconds emails are sent then. As you can see I'm pulling out like 20-30 emails currently out of my DB. I thought this was the correct way to send mails asynchronously, but apparently its not... How can I push the email sending to another thread so it doesn't affects the user who is using this page? Can someone help me out with this ? Thanks !

Upvotes: 3

Views: 4061

Answers (2)

Abhitalks
Abhitalks

Reputation: 28387

The reason is mentioned here on the MSDN documentation on SmtpClient.SendAsync

After calling SendAsync, you must wait for the e-mail transmission to complete before attempting to send another e-mail message using Send or SendAsync.

You could call SendAsync using ThreadPool thru a wrapped method call. This would give you something like a fire-and-forget scenario.

Something like this:

public void SendViaThread(System.Net.Mail.MailMessage message) {
    try {
        System.Threading.ThreadPool.QueueUserWorkItem(SendViaAsync, message);
    } catch (Exception ex) {
        throw;
    }
}

private void SendViaAsync(object stateObject) {
    System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient();
    System.Net.Mail.MmailMessage message = (MmailMessage)stateObject;
    ...
    smtp.Credentials = new NetworkCredential("...");
    smtp.Port = 587;
    smtp.Host = "...";
    ...
    smtp.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
    ...
    smtp.Send(message);
}

And you would call SendViaThread(mail) in your code. So, your loop now becomes:

for (int i = 0; i < lista.Count; i++) {
    MailMessage mail = new MailMessage();
    mail.From = new MailAddress("...");
    mail.To.Add(lista[i].Email);
    mail.Subject = "...";
    ...
    mail.Body = "...";
    SendViaThread(mail);
 }

Upvotes: 1

Jason
Jason

Reputation: 3030

If you want to not have your main thread waiting, try tossing it in a Task.

Task.Factory.StartNew( () => {
  // do mail stuff here
});

Be aware, though that each time you spawn a task, it'll spawn a new (or re-use) a thread that your system has made. If you're firing off 30 e-mails, you have the potential of firing off a lot of threads (the system has a programmable cap, too). Task.Factory.StartNew is a very simple way to do a fire-and-forget process.

http://msdn.microsoft.com/en-us/library/dd321439%28v=vs.110%29.aspx (in this example, the code is also keeping a collection of Tasks, which is nice if you care to manage them -- but in reality, you only need the Task.Factory.StartNew(()=>{ bit if you want to fire-and-forget. Just be careful because you don't want to have orphaned threads pegging your memory usage.

Upvotes: 1

Related Questions