2013-05-08 4 views
7

Wir haben einen (synchronen) E-Mail-Code, der eine Klasse erstellt, die einen SmtpClient erstellt und dann eine E-Mail sendet. Der SmtpClient wird nicht wiederverwendet; jedoch erhalten wir die folgende Ausnahme ab und zu:Warum erhalten wir regelmäßig "Ein asynchroner Anruf ist bereits in Bearbeitung" beim Aufruf von 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 

-Code sieht wie folgt aus:

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

Antwort

1

Meine Vermutung ist, dass die SmtpClient nicht gleichzeitig mehrere Nachrichten senden wurde entwickelt.

würde ich die Klasse ändern wie diese statt:

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

Da es wirklich keinen Grund, sind die Client im Konstruktor zu erstellen.

Ich änderte auch, so dass die Klasse ArgumentNullException und nicht ExceptionfromMailAddress 0 ist null ist. Ein leeres Exception nicht viel sagen ..

aktualisiert

Der Code kann nun einen Thread-Pool-Thread für das Senden (und Wiederverwendung des SmtpClient) verwenden.

+0

Oder anstatt eine neue SmtpClient-Instanz zu erstellen, kann das OP dieses Objekt einfach sperren, bis die Nachricht gesendet wird. – Marco

+0

Ja, es gibt einen guten Grund, den Client im Konstrukt zu erstellen: Wenn Sie mehrere E-Mails an denselben Server senden, werden die Verbindungen gepoolt. Weitere Informationen finden Sie unter http://msdn.microsoft.com/en-us/library/system.net.mail.smtpclient.dispose.aspx –

+0

@ErnodeWeerd: Sehen Sie den aktualisierten Code. – jgauffin

5

Sie müssen die SmtpClient entsorgen Dispose verwenden, using oder durch das Einweg-Muster für Ihre Klasse EmailSender Implementierung (die besser geeignet, weil man hier die Lebensdauer des SmtpClient auf die Lebensdauer des EmailSender im Konstruktor zu binden.)

Das könnte diese Ausnahme lösen.

+0

Ich stimme zu, wir sollten Dispose, aber könnte es wirklich dieses Problem verursachen? –

+0

Es könnte. Die "hin und wieder" von Ihrer Frage könnte mit Timeouts, Aufräumen, ... zusammenhängen Die richtige Ressourcenverwaltung und zu sehen Wenn die Ausnahme weiterhin angezeigt wird, ist es schwierig, das Problem zu lokalisieren, wenn Sie es nicht reproduzieren können. –

Verwandte Themen