How to Send Bulk Emails?

Hello. My company is evaluating the use of Aspose.Email to enhance a .NET Framework console application that we use to send corporate communication emails, via an internal Microsoft Exchange Server based SMTP server, using System.Net.Mail. The program will often send emails to upwards of 20,000 recipients, so in order to only send ~100 actual email messages out instead of 20,000, we split the collection of email addresses into batches of ~200, and each email message has those 200 addresses included in the BCC.

We now have the desire to send individually crafted email messages, formatted specifically for each recipient, instead of the same message to all recipients like we do today. Could you please let me know if Aspose.Email has functionality, perhaps using BulkSend, that would allow us to send the 20,000 individual message to our SMTP server, without overloading it, and able to do so in a timely manner? (today the 100 or so emails are sent over a time period of around 20-30 minutes). We would like technical information around how exactly Aspose handles that situation, in regards to how it sends bulk email messages to the SMTP server. Thanks!

@ggorczow

The API does support sending bulk emails. Please visit this documentation link for your kind reference.

The documentation does not provide any technical information around what BulkSend does exactly, in regards to how it sends a batch of emails using only one message to the SMTP server. We would like this information before we proceed with evaluating the product. Can you please supply this information to me?

Can you elaborate though on how this works under the covers and the benefits or best practices around how best to use it? The link just basically goes to code samples and this scenario is obviously a hard one to try out without actually doing a large scale email.

For example, for a large scale communication (17k+ addresses), should the code example for “Sending Bulk Emails” be used or instead should “Sending Emails with Multiconnection” be followed? If the latter, is 5 the recommended value to stick with or is there guidance to follow to determine what value should be used?

Appreciate any deeper explanation or guidance of how to approach this scenario with aspose.

@rstone,

We may not divulge the internal API working with you but the documentation link that I have shared exhibit how to manage sending bulk emails. However, we are internally discussing it and will share any possibility of providing technical details with you if we can.

Thanks. We would be open to an offline call if that would be a more appropriate medium to discuss?

@rstone

I regret to share that we may not have that offline session. We first need to evaluate whether we can share the technical internal information with you or not. We will share feedback with you as soon as possible in this regard.

Hi Aspose Technical Team,
I am evaluating the BulkSend capability and wanted to understand, what will happen if there is some kind of exception possibly a timeout when performing the bulk send.
In this event what is the recommendation and best practice ? Is there any way to prevent partial sending of mails when this happens?
Also current documentation only mentions to use tracing in the catch block. I would like to understand how the send is working and if there is a better mechanism to handle Aspose Exceptions if the bulk send does not complete.

@fgomes

The bulk send operation informs about failed and uncompleted operation by throwing an exception. otherwise, if exception is not thrown it means that all messages have been sent correctly.
You may receive, TimeoutException or SmtpException in case of error. Both of them have ErrorDetails property that will contains SendMessagesResult object. If you get an AsposeException, you will have to check inner exception to get TimeoutException or SmtpException.
SendMessagesResult contains information about successfully handled messages, failure sendings and not handled messages. If you are interested in other mechanism to be informed about sending messages (for instance by using event) we may implement this by request.

Hi Muddassir,
As part of building a retry logic for the BulkSend operation here are two cases that I have tried

  1. BulkSend operation with a Timeout- I received AsposeException in this case with partial messages delivered.
    here is the screenshot - ASPOSE_Timeout_Exception.PNG (34.3 KB)
    The Inner Exception and Error details property are both null and I have have no way to check how many messages were not delivered.

  2. BulkSend operation with disconnect from Internet - I got a generic Exception in this case as shown in the screenshot
    Capture2_iNTERNET_DISCONNECT.PNG (31.2 KB)

In both the above cases I am not able to identify how many messages were undelivered, so that I could retry to send the undelivered messages in these events.

Please can you provide a sample for these cases or let me know in case I am missing something from my end.

@fgomes

After observing the information shared, we need to further investigate this on our end for that we request you to please share the working source code reproducing the issue on your end.

Hi Muddassir,
Below is the code that I am trying to implement the Retry logic .I get null in the inner exception and error details property .

    try
        {                

             //Logic to populate the bulkMessageForAllRegions 

            if (bool.Parse(ConfigurationManager.AppSettings["SendEmails"]) && 
                                                                   bulkMessageForAllRegions !=null)
            {
              // creat the SMTP client
                client.Send(bulkMessageForAllRegions);       

            }
               
            
        }
        catch (Aspose.Email.AsposeException asposeException)
        {
          Trace.WriteLineIf(textLogging.Enabled, DateTime.Now.ToString() + "*** ERROR OCCURED: " 
           + asposeException.Message + " ***  Aspose Bulk Send Mail failed");
          System.Threading.Thread.Sleep(60000); 
         
         //NEED TO ADD RETRY LOGIC HERE
           
        }
        catch (Exception ex)
        {
            if (!ex.Message.Contains("timeout"))
            {
                Trace.WriteLineIf(textLogging.Enabled, DateTime.Now.ToString() + " Error: " + 
                  ex.Message);
                SendErrorNotification(ex);
            }
        }

Hi Muddassir,
Any update for the code shared above?

@fgomes

We had added a ticket with ID EMAILNET-40117 in our issue tracking system to further investigate the issue as it required investigation. I request for your patience and will share the good news with you as soon as issue will be addressed.

Hi Muddassir,
Do we have any update on the investigation for ticket ID EMAILNET-4011?

Hi Muddassir,
Any update on this so far?

@fgomes

We have made some changed in API. There are Obsolete members and classes.

class SendMessagesResult
event EventHandler<MailMessageEventArgs> SmtpClient.SucceededQueueSending
event EventHandler<MailMessageEventArgs> SmtpClient.FailedQueueSending

The new members and classes:

event EventHandler<MailMessageEventArgs> SmtpClient.SucceededSending

For some reasons there are some changes in error handling.
Class SendMessagesResult has been marked as obsolete.
More optimal way is using SucceededSending event to take into account messages which have been sent successfully.

Please use the following example:

            using (SmtpClient smtpClient = TestUtil.CreateSmtpClient(user))
            {
                string sguid = Guid.NewGuid().ToString();
                string subjectPrefix = $"EMAILNET-40117 - {sguid} - ";
                int cnt = 0;
                smtpClient.SucceededSending += delegate (object sender, MailMessageEventArgs evntArgs)
                {
                    Assert.IsNotNull(evntArgs);
                    Assert.IsNotNull(evntArgs.Message);
                    Assert.IsTrue(evntArgs.Message.Subject.StartsWith(subjectPrefix));
                    cnt++;
                };
                int msgNumber = 30;
                MailMessage[] messages = new int[msgNumber].Select(i =>
                {
                    MailMessage message = new MailMessage(user.EMail, user.EMail, subjectPrefix + i, "");
                    message.Attachments.Add(new Attachment(new MemoryStream(new byte[1024]), "Attachment 1"));
                    return message;
                }).ToArray();
                smtpClient.Send(messages);
                Thread.Sleep(3000);
                Assert.AreEqual(msgNumber, cnt);
                cnt = 0;
                messages = new int[msgNumber].Select(i =>
                {
                    MailMessage message = new MailMessage(user.EMail, user.EMail, subjectPrefix + i, "");
                    message.Attachments.Add(new Attachment(new MemoryStream(new byte[1024]), "Attachment 1"));
                    return message;
                }).ToArray();
                try
                {
                    smtpClient.Timeout = 3000;
                    smtpClient.Send(messages);
                }
                catch (TimeoutException)
                {
                    Assert.Less(cnt, msgNumber);
                }
            }

The issues you have found earlier (filed as EMAILNET-40117) have been fixed in this update.

Hi, this code looks like it’s for use in an advanced .NET Framework Version. Is there C# code I can use that will work with projects using .NET Framework 4.6.1 ?

@GlennIM,
Welcome to our community! Thank you for posting the query. I checked and found that this code example is compilable for .NET Framework 4.6.1 as well. Please describe the difficulties you encountered.