首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在ASP.NET中异步发送电子邮件的正确方法...(我做得对吗?)

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

Stack Overflow用户
提问于 2012-01-06 02:09:02
回答 7查看 33.6K关注 0票数 20

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

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

假设我有一个方法,比如:

代码语言:javascript
复制
private void SendTheMail() { // Stuff }

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

代码语言:javascript
复制
Emailer mailer = new Emailer();
Thread emailThread = new Thread(() => mailer.SendTheMail());
emailThread.Start();

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

我的下一个想法是对异步委托做一些研究。这似乎工作得更好,因为异常是在异步委托中处理的(与上面的线程示例不同)。然而,我担心如果我做错了,或者我可能导致了内存泄漏。

这就是我要做的:

代码语言:javascript
复制
Emailer mailer = new Emailer();
AsyncMethodCaller caller = new AsyncMethodCaller(mailer.SendMailInSeperateThread);
caller.BeginInvoke(message, email.EmailId, null, null);
// Never EndInvoke... 

我这样做对吗?

EN

回答 7

Stack Overflow用户

回答已采纳

发布于 2012-01-06 20:50:44

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

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

在对ASawyer的回应中,我完全惊讶于客户端中内置了一个.SendAsync。我尝试了一段时间的解决方案,但它似乎没有为我做的把戏。尽管我可以跳过执行SendAsync的代码的客户端,但页面仍然会“等待”,直到SendCompleted事件完成。我的目标是让用户和页面向前移动,同时在后台发送电子邮件。我有一种感觉,我可能还是做错了什么。因此,如果有人遇到这种情况,他们可能想自己尝试一下。

这是我如何100%异步发送电子邮件的完整解决方案,以及ELMAH.MVC错误日志记录。我决定使用示例2的扩展版本:

代码语言:javascript
复制
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.")));
    }
}
票数 25
EN

Stack Overflow用户

发布于 2014-03-18 14:02:12

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

代码语言:javascript
复制
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);
    }
} 
票数 6
EN

Stack Overflow用户

发布于 2012-01-06 02:22:12

如果你正在使用.Net的SmtpClient和MailMessage类,你应该注意一些事情。首先,预计发送时会出现错误,因此捕获并处理它们。其次,在.Net 4中,这些类有一些变化,现在都实现了IDisposable (MailMessage从3.5开始,SmtpClient在4.0中是新的)。因此,您创建的SmtpClient和MailMessage应该使用块进行包装或显式处理。这是一些人没有意识到的突破性变化。

有关使用异步发送时的处理的更多信息,请参阅此SO问题:

What are best practices for using SmtpClient, SendAsync and Dispose under .NET 4.0

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8747483

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档