2013-05-08 1 views
7

У нас есть (синхронный) код электронной почты, который создает класс, который создает SmtpClient, а затем отправляет электронное письмо. SmtpClient не используется повторно; Однако мы получаем следующее исключение каждый сейчас и потом:Почему мы периодически получаем «Асинхронный вызов уже выполняется» при вызове SmtpClient.Send?

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 

код выглядит следующим образом:

// ... 
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); 
    } 
} 

ответ

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) 
     } 
    } 
} 

Поскольку не существует на самом деле никаких оснований для создания клиента в конструкторе.

Я также изменил так, что класс бросает ArgumentNullException, а не Exception если fromMailAddress - null. Пустой Exception ничего не говорит ..

Update

Код этого теперь используют пул потоков поток для отправки (и повторного использования SmtpClient).

+0

Или вместо создания нового экземпляра SmtpClient OP может просто заблокировать этот объект, пока сообщение не будет отправлено. – Marco

+0

Да, есть хорошая причина для создания клиента в конструкторе: при отправке нескольких писем на те же серверные соединения будут объединены. См. Раздел замечаний на http://msdn.microsoft.com/en-us/library/system.net.mail.smtpclient.dispose.aspx –

+0

@ErnodeWeerd: см. Обновленный код. – jgauffin

5

Вы должны избавиться от SmtpClient использования Dispose, using или пути осуществления одноразового использования шаблона для класса почтового сообщения (которые здесь более уместны, потому что вы зафиксируете срок службы SmtpClient на время жизни почтового сообщения в конструкторе.)

Это может решить это исключение.

+0

Я согласен, что мы должны Dispose, но может ли это действительно вызвать эту проблему? –

+0

Может быть. «Время от времени» из вашего вопроса может быть связано с тайм-аутами, очисткой, ... добавлением правильного управления ресурсами и см. если исключение все еще появляется. Трудно определить проблему, если вы не можете воспроизвести ее. –