HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Using SendAsync to send multiple emails

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
usingemailsmultiplesendsendasync

Problem

My method for sending emails with Send() is getting very slow when I send more than ten messages.

Sample

1 (1 sec)

2 (2 sec)

3 (4 sec)

4 (8 sec) and so on...

so I started to try SendAsync()

public Boolean sendSMTPMail(string subject, string body, customer[] recipients, string attachmentFileName = null)
{
    string css = "*{font-family: Arial, Helvetica, sans-serif;}";
    SmtpClient client = new SmtpClient();
    //some client settings..

    MailMessage mm = new MailMessage();
    mm.IsBodyHtml = true;
    mm.From = new MailAddress(MainForm.from, MainForm.fromName, Encoding.UTF8);
    mm.Subject = subject;

    foreach (var recipient in recipients)
    {
        Application.DoEvents();
        mm.To.Clear();
        mm.To.Add(new MailAddress(recipient.getEmail()));
        mm.AlternateViews.Add(getEmbeddedImages(css + doReplacements(body, recipient)));

        try
        {
            //client.Send(mm);
            client.SendAsync(mm, "userToken");
            //client.SendMailAsync(mm);
        }
        catch (Exception)
        {
            return false;
        }
    }
    return true;
}


Additional Information about my methods:

getEmbeddedImages replaces imagePath with needed cid:xxxx and adds the image so it can be sent as a part of the email and not only as attachment.

doReplacements replaces strings in my body to get the email text more recipient specific:

Hello my name is %name%,
I am %age% years old.


will be translated to

Hello my name is John Doe,
I am 100 years old.


Questions:

  • Is this the right way to do it?



  • Is it important to send different userTokens?



  • For what is this token needed anyway? Obviously it doesn't matter what I fill in there.

Solution

From the SendAsync docs:


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.

So no, this may not work properly. You need to register an event handler on the SmtpClient.SendCompleted event so that you know the message has sent successfully.

I'm going to be frank about this, the SmtpClient has an awful API. Your confusion is not your fault.

Your best bet would be to create a dictionary of recipients to use as a queue.

  • Send the first message and pass the dictionary key in as the token.



  • When the message completed event occurs, use the token to remove the recipient from the dictionary and send the next message.



  • Repeat until the queue is empty.



This is going to be a slow, fairly synchronous process, so you may want to spin up a background thread or task to run this on, if your program has other work to do while this is going on.

I really encourage you to thoroughly read through the documentation I linked to. It's an old part of the framework and not very user (or async) friendly.

Or forget all that and just use the newer SendMailAsync(MailMessage) method.

Just be sure to await the result.

try
    {
        await client.SendMailAsync(mm);
    }


Which means that you'll need to modify the signature of your method to be compatible with async/await.

Code Snippets

try
    {
        await client.SendMailAsync(mm);
    }

Context

StackExchange Code Review Q#150766, answer score: 8

Revisions (0)

No revisions yet.