7
\$\begingroup\$

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 = "<style>*{font-family: Arial, Helvetica, sans-serif;}</style>";
    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:

  1. Is this the right way to do it?
  2. Is it important to send different userTokens?
  3. For what is this token needed anyway? Obviously it doesn't matter what I fill in there.
\$\endgroup\$
  • 3
    \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$ – Mathieu Guindon Dec 28 '16 at 16:17
7
\$\begingroup\$

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.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ i know that i have to bind the complete event... but the documentation says This method does not block the calling thread and allows the caller to pass an object to the method that is invoked when the operation completes. My recipients are stored in kind of a object array (see foreach) having email, username, customernumber and stuff like that. Why does sending will take long ? \$\endgroup\$ – Dwza Dec 25 '16 at 0:24
  • \$\begingroup\$ Because it has to connect to your smtp server, which in turn needs to actually send the message, receive a receipt from the receiving server, then send that confirmation back to your application. That's 1-2-3-4 (possibly many more) jumps over several networks. That's a slow operation, no way around it. \$\endgroup\$ – RubberDuck Dec 25 '16 at 0:30
  • \$\begingroup\$ @Dwza I cannot encourage you enough to go read the example code in the docs and think about how you could create a long running, yet asynchronous, process using events and a queue. \$\endgroup\$ – RubberDuck Dec 25 '16 at 0:32
  • \$\begingroup\$ I think part of the confusion is that the method is named SendAsync, but it doesn't return a Task, so it can't be awaited like most newer methods with that name. This API does asynchronous the old fashioned way. \$\endgroup\$ – RubberDuck Dec 25 '16 at 0:34
  • \$\begingroup\$ So is it the same if i simply use SendMailAsync() ? \$\endgroup\$ – Dwza Dec 25 '16 at 0:35
1
\$\begingroup\$

Some quick remarks:

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.