SmtpClient SendAsync错误

时间:2017-07-13 14:17:29

标签: c# email asynchronous smtp rabbitmq

背景

我编写了一个小型控制台应用程序来监控Rab​​bitMQ队列中的电子邮件。每当电子邮件被推送到队列时,我的应用程序就会接收该电子邮件,处理它并发送它。

CODE

以下是我的电子邮件服务的代码,它实际上是发送电子邮件的。

public class MailService : IMailService
{
    private readonly SmtpClient _smtpClient = new SmtpClient();

    public void SendEmail(string toEmail, string subject, string body, bool isHtml)
    {
      var emailMessage = BuildEmailMessage(toEmail.Trim(), subject.Trim(), body, isHtml);
      _smtpClient.SendAsync(emailMessage, null);
    }

    #region Helpers
    private static MailMessage BuildEmailMessage(string toEmail, string subject, string body, bool isHtml)
    {
      const string fromEmailAddress = "james.brown@world.com";
      var emailMessage = new MailMessage(fromEmailAddress, toEmail, subject, body) { IsBodyHtml = isHtml };
      return emailMessage;
    }
    #endregion
}

问题

我在RabbitMQ队列上发了2封电子邮件,当我启动我的控制台应用程序使用者时,它在发送第一封电子邮件后发出了以下异常(我收到了收件箱中的第一封电子邮件)。

  

异步调用已在进行中。必须先完成或取消,然后才能调用此方法。

我做了一些挖掘,并看到this thread解释了为什么我得到了这个。显然使用SendAsync()是不可取的,因为:

  

调用SendAsync后,您必须等待电子邮件传输完成,然后再尝试使用Send或SendAsync发送其他电子邮件。

那么推荐的解决方法是什么?我可以为每封电子邮件创建SmtpClient类的新实例,但这真的是个好主意吗?

1 个答案:

答案 0 :(得分:1)

同步发送电子邮件,无需使用SendAsync的旧的异步语法。此外,请确保仅通过将SmtpClient包装在using语句中,一次只能从一个线程中获取MailService.SendEmail。有轻微的性能损失,但除非您发送大量电子邮件,否则您可能无法注意到它。如果你发送了一个吨,那么重载你的IEnumerable<EmailModel>方法接受一个public void SendEmail(string toEmail, string subject, string body, bool isHtml) { var emailMessage = BuildEmailMessage(toEmail.Trim(), subject.Trim(), body, isHtml); using(var client = new SmtpClient()) { _smtpClient.Send(emailMessage); } } //this would be the right way to do async public async Task SendEmailAsync(string toEmail, string subject, string body, bool isHtml) { var emailMessage = BuildEmailMessage(toEmail.Trim(), subject.Trim(), body, isHtml); using(var client = new SmtpClient()) { _smtpClient.SendMailAsync(emailMessage); } } //this would be the right way to do multiple emails //you'd need to create an EmailModel class to contain all the details for each email (similar to MailMessage, but it would prevent your own code from taking a dependency on System.Net.Mail public void SendEmail(IEnumerable<EmailModel> emailModels) { var mailMessages = emailModels.Select(em => ConvertEmailModelToMailMessage(em)); using(var client = new SmtpClient()) { foreach(var mailMessage in mailMessages) { //you may want some error handling on the below line depending on whether you want all emails to attempt to send even if one encounters an error _smtpClient.Send(mailMessage); } } } private MailMessage ConvertEmailModelToMailMessage(EmailModel emailModel) { //do conversion here } 并使用一个SmtpClient一次性发送它们。

Component