在ASP.NET中异步发送电子邮件的正确方法是什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (184)

当用户在我的网站上注册时,我不明白为什么我需要让他“等待”smtp才能通过,以便获得激活电子邮件。

我决定我想要异步启动这个代码,这是一次冒险。

让我们想象我有一个方法,比如:

private void SendTheMail() { // Stuff }

我的第一次虽然..是线程。我这样做了:

Emailer mailer = new Emailer();
Thread emailThread = new Thread(() => mailer.SendTheMail());
emailThread.Start();

这工作...直到我决定测试它的错误处理能力。我故意打破了我的web.config中的SMTP服务器地址并尝试了它。可怕的结果是,IIS基本上BARFED在w3wp.exe上有一个未处理的异常错误(这是一个Windows错误!有多极端......)ELMAH(我的错误记录器)没有捕获它并且IIS重新启动,所以网站上的任何人都有他们的会话被删除。完全不可接受的结果!

我的下一个想法是对异步代表进行一些研究。这似乎更好,因为异常正在异步委托内处理(不像上面的线程示例)。但是,我担心如果我做错了,或者我正在造成内存泄漏。

以下是我正在做的事情:

Emailer mailer = new Emailer();
AsyncMethodCaller caller = new AsyncMethodCaller(mailer.SendMailInSeperateThread);
caller.BeginInvoke(message, email.EmailId, null, null);
// Never EndInvoke... 

我做得对吗?

提问于
用户回答回答于

我在这里提出了很多很好的建议,例如确保记住使用IDisposable(我完全不知道)。我还意识到,在另一个线程中手动捕获错误是非常重要的,因为没有上下文 - 我一直在研究理论,我应该让ELMAH处理所有事情。此外,进一步的探索使我意识到我忘记了在邮件消息上使用IDisposable。

作为对Richard的回应,尽管我发现线程解决方案可以工作(正如我的第一个示例中所提到的),只要我能够捕捉到错误...仍然有些可怕的事实是IIS完全爆炸,如果错误不是'抓住了。这告诉我,ASP.NET / IIS从来没有为你这么做......这就是为什么我倾向于继续使用.BeginInvoke /委托,因为这不会搞砸IIS出现问题时,似乎在ASP.NET中更受欢迎。

为了回应ASawyer,我完全惊讶的发现SMTP客户端中内置了.SendAsync。我玩了一段时间的解决方案,但它似乎并没有为我做伎俩。尽管我可以跳过执行SendAsync的代码的客户端,但页面仍然会“等待”,直到SendCompleted事件完成。我的目标是在电子邮件在后台发送时让用户和页面前进。我有一种感觉,我可能仍然在做一些错误的事情......所以如果有人来这里,他们可能会想自己尝试。

这里是我的完整解决方案,除了ELMAH.MVC错误日志以外,我还发送了100%的异步电子邮件。我决定采用示例2的扩展版本:

public void SendThat(MailMessage message)
{
    AsyncMethodCaller caller = new AsyncMethodCaller(SendMailInSeperateThread);
    AsyncCallback callbackHandler = new AsyncCallback(AsyncCallback);
    caller.BeginInvoke(message, callbackHandler, null);
}

private delegate void AsyncMethodCaller(MailMessage message);

private void SendMailInSeperateThread(MailMessage message)
{
    try
    {
        SmtpClient client = new SmtpClient();
        client.Timeout = 20000; // 20 second timeout... why more?
        client.Send(message);
        client.Dispose();
        message.Dispose();

        // If you have a flag checking to see if an email was sent, set it here
        // Pass more parameters in the delegate if you need to...
    }
    catch (Exception e)
    {
         // This is very necessary to catch errors since we are in
         // a different context & thread
         Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
    }
}

private void AsyncCallback(IAsyncResult ar)
{
    try
    {
        AsyncResult result = (AsyncResult)ar;
        AsyncMethodCaller caller = (AsyncMethodCaller)result.AsyncDelegate;
        caller.EndInvoke(ar);
    }
    catch (Exception e)
    {
        Elmah.ErrorLog.GetDefault(null).Log(new Error(e));
        Elmah.ErrorLog.GetDefault(null).Log(new Error(new Exception("Emailer - This hacky asynccallback thing is puking, serves you right.")));
    }
}
用户回答回答于

从.NET 4.5开始,SmtpClient实现了异步等待方法SendMailAsync。因此,异步发送电子邮件的方式如下所示:

public async Task SendEmail(string toEmailAddress, string emailSubject, string emailMessage)
{
    var message = new MailMessage();
    message.To.Add(toEmailAddress);

    message.Subject = emailSubject;
    message.Body = emailMessage;

    using (var smtpClient = new SmtpClient())
    {
        await smtpClient.SendMailAsync(message);
    }
} 

扫码关注云+社区

领取腾讯云代金券