在ASP.NET中异步发送电子邮件的正确方法...(我做得对吗?)

Ral*_*h N 20 c# asp.net email asynchronous

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

我决定以异步方式启动此代码,这是一次冒险.

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

private void SendTheMail() { // Stuff }
Run Code Online (Sandbox Code Playgroud)

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

Emailer mailer = new Emailer();
Thread emailThread = new Thread(() => mailer.SendTheMail());
emailThread.Start();
Run Code Online (Sandbox Code Playgroud)

这工作......直到我决定测试它的错误处理能力.我故意破坏了我的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... 
Run Code Online (Sandbox Code Playgroud)

我这样做了吗?

Ral*_*h N 25

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

在回应理查德的时候,虽然我看到线程解决方案可以工作(正如我的第一个例子所示),只要我抓住了错误......但是如果那个错误不是,那么IIS完全爆炸的事实仍然是可怕的.抓住了.这告诉我ASP.NET/IIS从来没有意味着你这样做...这就是为什么我倾向于继续使用.BeginInvoke/delegates而不是因为当出现问题时它不会弄乱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.")));
    }
}
Run Code Online (Sandbox Code Playgroud)


Bor*_*itz 6

从.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);
    }
} 
Run Code Online (Sandbox Code Playgroud)