为什么我们在调用SmtpClient.Send时会定期收到"异步调用已在进行中"?

Dan*_*eny 9 .net c# smtpclient

我们有一些(同步)电子邮件代码,它创建一个创建SmtpClient的类,然后发送一封电子邮件.SmtpClient不会被重用; 但是我们偶尔得到以下异常:

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
Run Code Online (Sandbox Code Playgroud)

代码如下所示:

// ...
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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Ern*_*rno 5

您需要使用,或通过为您的类EmailSender实现一次性模式来处置SmtpClient(这在此更合适,因为您将SmtpClient的生命周期与构造函数中的EmailSender的生命周期联系起来.)Disposeusing

这可能会解决这个例外.


jga*_*fin 1

我的猜测是,它的SmtpClient设计目的不是同时发送多条消息。

我会像这样更改类:

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因为确实没有理由在构造函数中创建客户端。

我还进行了更改,以便类抛出ArgumentNullException而不是ExceptioniffromMailAddress为空。空了Exception就不多说了..

更新

该代码现在使用线程池线程进行发送(并重用 smtpclient)。