8
\$\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\$
1
  • 3
    \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$ Commented Dec 28, 2016 at 16:17

2 Answers 2

8
\$\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.

\$\endgroup\$
7
  • \$\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
    Commented Dec 25, 2016 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
    Commented Dec 25, 2016 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
    Commented Dec 25, 2016 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
    Commented Dec 25, 2016 at 0:34
  • \$\begingroup\$ So is it the same if i simply use SendMailAsync() ? \$\endgroup\$
    – Dwza
    Commented Dec 25, 2016 at 0:35
1
\$\begingroup\$

Some quick remarks:

\$\endgroup\$
2
  • 1
    \$\begingroup\$ This is matter for a comment, not for an answer. \$\endgroup\$
    – sergiol
    Commented Mar 11 at 17:23
  • \$\begingroup\$ 1) But I want to 2) Because I can :P 3) Should, not must! I also think thats wrong. In my mind Classes are PascalCase and methods are camelCase. 4) I actually more stick with PSR instead of MS conventions since I am coding in othere languages as well :D But thx (even doe my thanks came late after 8 years :D ) \$\endgroup\$
    – Dwza
    Commented Mar 12 at 15:36

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