4

I've a problem with sending a newsletter (to about 5000 people) in my mvc-helper class.

public static void SendNewsletter(string fromAccount, string subject, string eMailText)
{
    var userEMailAddresses = new List<string>();

    using (var dbContext = new dbEntities())
    {
        userEMailAddresses =
            dbContext
                .User
                    .Where(w =>
                        !w.IsDeactivated &&
                        !w.IsBlacklisted)
                    .Select(s =>
                        s.UserName)
                    .ToList();
    }

    new Thread(() =>
    {
        for (int i = 0; i < userEMailAddresses.Count; i++)
        {
            SendMail(fromAccount, userEMailAddresses[i], subject, eMailText);
        }
    }).Start();
}

This is my function which will be called in a controller. The next Code-Block is the sending function.

public static void SendMail(string fromAccount, string toAccount, string subject, string eMailText)
{
    var fromEmailAddress = new EMailModel(fromAccount);

    var body = string.Empty;
    using (var sr = new StreamReader(HostingEnvironment.MapPath("\\App_Data\\Templates\\") + "EMailTemplate.txt"))
    {
        body = sr.ReadToEnd();
    }

    body = body.Replace("%%Subject%%", subject);
    body = body.Replace("%%Body%%", eMailText);
    body = body.Replace("%%Year%%", DateTime.Now.Year.ToString());

    using (MailMessage mail = new MailMessage(fromEmailAddress.EMailAddress, toAccount))
    {
        mail.Subject = subject;
        mail.Body = WebUtility.HtmlDecode(body);
        mail.IsBodyHtml = true;

        using (SmtpClient smtp = new SmtpClient(Statics.SmtpClient, Statics.SmtpPort))
        {
            smtp.UseDefaultCredentials = false;
            smtp.Credentials = new System.Net.NetworkCredential(fromEmailAddress.EMailAddress, fromEmailAddress.Password);
            smtp.EnableSsl = Statics.SslEnabled;
            try
            {
                smtp.Send(mail);
                Thread.CurrentThread.Join(1000);
            }
            catch (Exception ex) {
                throw ex;
            }
        }
    }
}

After approx 1300 emails, my IIS get the following error:

ErrorImage

How can I solve this problem? I need the newsletter system...

ekad
  • 14,436
  • 26
  • 44
  • 46
Henscho
  • 51
  • 5
  • 1
    `Thread.CurrentThread.Join(1000);` ??? – Falco Alexander May 26 '16 at 13:12
  • We have a mail system that just falls over after processing too many emails. It appeared our mail server was getting chocked up so we put in a loop counter and a hang time, ie process 10 emails, wait 10 seconds, rinse and repeat. It works fine now, maybe worth investigating in your case? – Hugo Yates May 26 '16 at 13:13
  • Personally I would be tempted to use a tried and tested asynchronous send and end rather than reinventing the wheel... such as EASendMail ... https://www.emailarchitect.net/easendmail/kb/csharp.aspx?cat=13 – Paul Zahra May 26 '16 at 13:16
  • As Hugo said.. ensure there is no spam filter at the email server, e.g. 100+ emails send in 1 minute = spam = block... are there any virus checks on the route? as they can also have spam filters. – Paul Zahra May 26 '16 at 13:19
  • @FalcoAlexander Thread.CurrentThread.Join(1000) is the same like Thread.Sleep(1000). – Henscho May 26 '16 at 13:19
  • What do the logs say about the iis worker process failing? – Paul Zahra May 26 '16 at 13:21
  • @PaulZahra No, all checks are disabled. – Henscho May 26 '16 at 13:21
  • @HenningHeusser why aren't you using the new *Async methods? Also, have you tried using the new (since .Net 4.5.2) `HostingEnvironment.QueueBackgroundWorkItem` ? – Nasreddine May 26 '16 at 13:22
  • @HenningHeusser join and sleep aren't quite the same http://stackoverflow.com/questions/19001755/difference-between-thread-sleepperiod-and-thread-currentthread-joinperiod – Paul Zahra May 26 '16 at 13:24
  • I advise to look into the machine.config of your IIS and consider field values such as maxIoThreads and maxWorkerThreads and also in aspnet.config at the values for maxConcurrentRequestsPerCPU, maxConcurrentThreadsPerCPU and requestQueueLimit – Paul Zahra May 26 '16 at 13:26
  • You should really consider usign something like [Hangifre](http://hangfire.io/) for jobs like these. – Nasreddine May 26 '16 at 13:27
  • What happens when changing or omitting the sleep? – Falco Alexander May 26 '16 at 13:29
  • Final comment before doing some work... http://stackoverflow.com/questions/15140308/how-to-send-mails-asynchronous – Paul Zahra May 26 '16 at 13:30

1 Answers1

0

I've found a lot of errors, why it doesn't work.

The first one was a security setting in MailEnable. On MailEnableAdmin -> Servers -> Localhost -> Servers and Connectors -> Rightclick on SMTP and Settings -> Security-Tab there was a checkmark on a limit of receipiants (1000). I've unchecked it and restarted the Server.

After this, there was a unvalid E-Mail-Address in my User-List. Then I've created an E-Mail-Check-Funtion:

    private static bool IsValidEMail(string eMailAddress)
    {
        if (Regex.IsMatch(eMailAddress, @"\A(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\Z", RegexOptions.IgnoreCase) && new EmailAddressAttribute().IsValid(eMailAddress))
            return true;

        return false;
    }

and I've updated my send-function to

    public static void SendMail(string fromAccount, string toAccount, string subject, string eMailText)
    {
        if (!IsValidEMail(toAccount))
        {
            var invalidEMailAddress = toAccount;

            toAccount = "administrator@YOUR-DOMAIN.com";
            subject = "E-Mail-Address is invalid";
            eMailText = String.Format("Dear Administrator,<br><br>the following E-Mail-Address is invalid:<br><br>{0}", invalidEMailAddress);
        }

        var fromEmailAddress = new EMailModel(fromAccount);

        var body = string.Empty;
        using (var sr = new StreamReader(HostingEnvironment.MapPath("\\App_Data\\Templates\\") + "EMailTemplate.txt"))
        {
            body = sr.ReadToEnd();
        }

        body = body.Replace("%%Subject%%", subject);
        body = body.Replace("%%Body%%", eMailText);
        body = body.Replace("%%Year%%", DateTime.Now.Year.ToString());

        using (MailMessage mail = new MailMessage(fromEmailAddress.EMailAddress, toAccount))
        {
            mail.Subject = subject;
            mail.Body = WebUtility.HtmlDecode(body);
            mail.IsBodyHtml = true;

            using (SmtpClient smtp = new SmtpClient(Statics.SmtpClient, Statics.SmtpPort))
            {
                smtp.UseDefaultCredentials = false;
                smtp.Credentials = new System.Net.NetworkCredential(fromEmailAddress.EMailAddress, fromEmailAddress.Password);
                smtp.EnableSsl = Statics.SslEnabled;
                try
                {
                    smtp.Send(mail);
                    Thread.CurrentThread.Join(1000);
                }
                catch (Exception) { }
            }
        }
    }

and the SendNewsletter-Function is the same. Now, all E-Mails go out.

Thanks a lot to all users who helped!

Henscho
  • 51
  • 5