Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we periodically get "An asynchronous call is already in progress" when calling SmtpClient.Send?

We have some (synchronous) email code, which creates a class that creates an SmtpClient, and then sends an email. The SmtpClient is not reused; however we get the following exception every now and then:

System.Web.HttpUnhandledException (0x80004005): Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.InvalidOperationException: An asynchronous call is already in progress. It must be completed or canceled before you can call this method.
   at System.Net.Mail.SmtpClient.Send(MailMessage message)
   at EmailSender.SendMail(MailAddress fromMailAddress, string to, String subject, String body, Boolean highPriority) in ...\EmailSender.cs:line 143

Code looks like this:

// ...
var emailSender = new EmailSender();
emailSender.SendMail(toEmail, subject, body, true);
// emailSender not used past this point
// ...

public class EmailSender : IEmailSender
{
    private readonly SmtpClient smtp;

    public EmailSender()
    {
        smtp = new SmtpClient();
    }

    public void SendMail(MailAddress fromMailAddress, string to, string subject, string body, bool highPriority)
    {
        if (fromMailAddress == null)
            throw new Exception();
        if (to == null)
            throw new ArgumentException("No valid recipients were supplied.", "to");

        // Mail initialization
        var mailMsg = new MailMessage
        {
            From = fromMailAddress,
            Subject = subject,
            Body = body,
            IsBodyHtml = true,
            Priority = (highPriority) ? MailPriority.High : MailPriority.Normal
        };

        mailMsg.To.Add(to);


        smtp.Send(mailMsg);
    }
}
like image 405
Danny Tuppeny Avatar asked May 08 '13 10:05

Danny Tuppeny


2 Answers

You need to dispose of the SmtpClient using Dispose, using or by implementing the disposable pattern for your class EmailSender (which more appropriate here because you are tying the lifetime of the SmtpClient to the lifetime of the EmailSender in the constructor.)

That might solve this exception.

like image 90
Emond Avatar answered Oct 23 '22 23:10

Emond


My guess is that the SmtpClient was not designed to send multiple messages concurrently.

I would change the class like this instead:

public class EmailSender : IEmailSender
{
    Queue<MailMessage> _messages = new Queue<MailMessage>();
    SmtpClient _client = new SmtpClient();

    public EmailSender()
    {
    }

    public void SendMail(MailAddress fromMailAddress, string to, string subject, string body, bool highPriority)
    {
        if (fromMailAddress == null)
            throw new ArgumentNullException("fromMailAddress");
        if (to == null)
            throw new ArgumentException("No valid recipients were supplied.", "to");

        // Mail initialization
        var mailMsg = new MailMessage
        {
            From = fromMailAddress,
            Subject = subject,
            Body = body,
            IsBodyHtml = true,
            Priority = (highPriority) ? MailPriority.High : MailPriority.Normal
        };

        mailMsg.To.Add(to);

        lock (_messages)
        {
            _messages.Enqueue(mailMsg);
            if (_messages.Count == 1)
            {
                ThreadPool.QueueUserWorkItem(SendEmailInternal);
            }
        }
    }

    protected virtual void SendEmailInternal(object state)
    {
        while (true)
        {
            MailMessage msg;
            lock (_messages)
            {
                if (_messages.Count == 0)
                    return;
                msg = _messages.Dequeue();
            }

            _client.Send(msg)
        }
    }
}

As there are really no reason to create the client in the constructor.

I also changed so that the class throws ArgumentNullException and not Exception if fromMailAddress is null. An empty Exception doesn't say much..

Update

The code do now use a thread pool thread for the sending (and reusing the smtpclient).

like image 31
jgauffin Avatar answered Oct 24 '22 01:10

jgauffin