使用 Asp.Net 发送异步电子邮件但 Page 仍在等待?

Posted

技术标签:

【中文标题】使用 Asp.Net 发送异步电子邮件但 Page 仍在等待?【英文标题】:Using Asp.Net to SendAsync emails but Page still waits? 【发布时间】:2011-06-29 14:56:05 【问题描述】:

我不希望用户等待页面完成发送过程,所以我想在 ASP.NET 3.5 中使用 SendAsync。但是在调试之后,我发现主线程 still 等待。

Main:调用发送邮件功能... 邮件发送:正在配置.... mailSend:设置不正确的端口.... mailSend:现在尝试发送.... 邮件发送:行尾 主要:电子邮件功能调用完成。 主要:过程完成! mailComp:发送邮件失败,将在 5 秒后重试... mailComp:正在重试... mailComp:发送成功! mailComp:行尾

现在我设置了错误的端口设置,所以第一封邮件失败,并测试第二次是否会成功。即使使用正确的端口,页面仍然在等待。只有在 mailComp 函数完成后,页面才会最终发布。这是对 SendAsyn 的一些限制吗?

这里有一些代码,不确定这是否有用。

protected void btnReset_Click(对象发送者,EventArgs e) 尝试 DataContext db = new DataContext(); var query = 来自 db.Fish 中的 u 其中 u.Username == txtUsername.Text & u.Email == txtEmail.Text 选择新的 u.Username, u.Email ; if (query.Count() != 0) 用户用户 = 新用户(); 字符串令牌 = user.requestPasswordReset(txtUsername.Text); 字符串 URL = Request.Url.AbsoluteUri.ToString() + "?token=" + token; String body = "如果您\n" + URL + "\n \n "则重置您的密码; untilty.SendEmail(txtEmail.Text, "重设密码", body); litTitle.Text = "消息已发送!"; litInfo.Text = "请在几分钟后查看您的电子邮件以获取进一步说明。"; 视图模式(假,假); 别的 litCannotFindUserWithEmail.Visible = true; 捕捉(例外前) Debug.Write("主要:异常:" + ex.ToString()); litInfo.Text = "我们目前遇到了一些技术难题。请稍后再试。如果您仍有问题,请致电 905344525"; /// 公共静态类实用程序 /// 公共静态无效发送电子邮件(字符串收件人,字符串主题,字符串正文) MailMessage 消息 = 新的 MailMessage(); message.To.Add(new MailAddress(recipient)); message.From = new MailAddress(fromaddress, "Basadur Profile Website"); message.Subject = 主题; 消息.正文 = 正文; 发信息); 私人静态无效发送(MailMessage 消息) SmtpClient smtp = new SmtpClient(); smtp.Host = "smtp.gmail.com"; smtp.Credentials = new System.Net.NetworkCredential(fromaddress, mailpassword); smtp.EnableSsl = true; Debug.WriteLine("mailSend: 设置了错误的端口...."); smtp.端口 = 5872; //错误的端口 smtp.SendCompleted += new SendCompletedEventHandler(smtp_SendCompleted); smtp.SendAsync(味精,味精); 静态无效 smtp_SendCompleted(对象发送者,System.ComponentModel.AsyncCompletedEventArgs e) var msg = (MailMessage)e.UserState; 如果(例如错误!= null) System.Threading.Thread.Sleep(1000 * 5); 尝试 SmtpClient smtp = new SmtpClient(); smtp.Host = "smtp.gmail.com"; smtp.Credentials = new System.Net.NetworkCredential(fromaddress, mailpassword); smtp.EnableSsl = true; smtp.端口 = 587; smtp.Send(msg); 捕捉(例外前) Debug.WriteLine("mailComp: 第二次放弃失败。");

【问题讨论】:

您希望“主要”执行在哪里恢复?它不能在 SendAsync 调用之前重新启动。 好吧,我在想,当我使用 SendAsyc 时,Main 执行将恢复并设置一个标签来表示“消息发送”,然后发布给用户。然后用户将重新获得控制权,即使发送进程仍在服务器的后台工作。我的调试告诉我这一切都是异步发生的,但 Main 在发布之前仍然等待。 不同的话题,你绝对不应该在btnReset_Click里面做那种工作。您应该从表示层传递到业​​务服务层。此外,除了与业务无关的数据层之外,您永远不应该在任何地方直接使用 EF 上下文。在表示层中使用 EF 上下文是现有的最糟糕的反模式之一。 尝试将同步方法放在 Task.Run 块下,例如 Task.Run(()=>Your method here);或使用 async-await 调用使您的方法异步。在最新的 .net 框架中,这些功能可用。 【参考方案1】:

在 .NET 4.5.2 中,添加了一种用于在后台调度任务的方法,独立于任何请求:HostingEnvironment.QueueBackgroundWorkItem()。

HostingEnvironment.QueueBackgroundWorkItem() 位于 System.Web.Hosting 命名空间中。

文档中的备注说:

不同于 ASP.NET 可以保留的普通 ThreadPool 工作项 跟踪当前有多少通过此 API 注册的工作项 运行,并且 ASP.NET 运行时将尝试延迟 AppDomain 关闭 直到这些工作项完成执行。

也就是说,您可以以一种即发即弃的方式启动任务,更有信心在应用域回收时它不会被终止。

用法如下:

HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>

    try 
    
       // do work....
    
    catch(Exception)
    
        //make sure nothing can throw in here
    
);

【讨论】:

这应该可行,我将自己尝试使用它,但请注意,在使用它的域拆解方面存在一些问题,在服务器重启时,工作可能会或可能不会完成。 【参考方案2】:

如今,您可以使用 Task 将工作分派到线程池。只需使用

Task.Run(()=>  Untilty.SendEmail(txtEmail.Text, "Reseting Password", body));

【讨论】:

【参考方案3】:

调试器似乎强制事情是单线程的。如果您单步执行,您可能会发现最终会在两个不同的文件之间跳转,因为每一步都会移动不同的线程。

尝试在那里添加一些日志并在不调试的情况下运行。

【讨论】:

调试器确实可以很好地处理线程,它可以得到非常多毛的调试方法,这些方法被同时调用(当你一直按 F10 时同时调试多个堆栈帧)。我相信 VS2015 试图改善这一点。 @ChrisMarisic 很大程度上取决于VS版本,我五年前的答案是针对VS2008的,这就是问题所在。【参考方案4】:

使用下面的代码,您可以使用 SmtpClient 在 asp.net 中发送异步电子邮件。

ThreadPool.QueueUserWorkItem(callback =>
                
                    var mailMessage = new MailMessage
                    
                        ...
                    ;

                    //if you need any references in handlers
                    object userState = new ReturnObject
                    
                        MailMessage = mailMessage,
                        SmtpClient = smtpServer
                    ;
                    smtpServer.SendCompleted += HandleSmtpResponse;
                    smtpServer.SendAsync(mailMessage, userState);
                );

【讨论】:

【参考方案5】:

使用线程:

// Create the thread object, passing in the method
  // via a ThreadStart delegate. This does not start the thread.
  Thread oThread = new Thread(new ThreadStart(SendMyEmail));

  // Start the thread
  oThread.Start();

Here 是源码,它有完整的线程教程。

【讨论】:

是的,但是当您在新线程中启动它时,您会在应用程序中获得多任务处理,即正在进行的线程正常继续,另一个线程会为您处理电子邮件发送。 这就是 SendAsync 已经做的。没有理由通过手动管理线程来使其复杂化。 但正如 OP 所说,页面等待,这意味着 SendAsync 不起作用。 不要在 ASP.NET 中执行此操作...如果线程中有未处理的异常,IIS 会出错

以上是关于使用 Asp.Net 发送异步电子邮件但 Page 仍在等待?的主要内容,如果未能解决你的问题,请参考以下文章

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

发送电子邮件 asp.net c#

使用 ASP.Net 从 HTML 发送电子邮件

如何发送电子邮件[重复]

如何在asp .net mvc中安排发送电子邮件

在 Asp.Net 中使用电子邮件发送忘记密码