背景
我编写了一个小型控制台应用程序来监控RabbitMQ队列中的电子邮件。每当电子邮件被推送到队列时,我的应用程序就会接收该电子邮件,处理它并发送它。
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
类的新实例,但这真的是个好主意吗?
答案 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