C# 使用Asp.Net发送异步电子邮件,但页面仍在等待?

C# 使用Asp.Net发送异步电子邮件,但页面仍在等待?,c#,asp.net,.net-3.5,asynchronous,sendasync,C#,Asp.net,.net 3.5,Asynchronous,Sendasync,我不希望用户等待页面完成发送过程,因此我考虑在ASP.NET3.5中使用SendAsync。但是在调试之后,我发现主线程仍然在等待 主要功能:呼叫发送电子邮件功能。。。 mailSend:正在配置。。。。 mailSend:设置不正确的端口。。。。 mailSend:现在尝试发送。。。。 mailSend:行尾 主要功能:电子邮件功能调用完成。 主:过程完成! mailComp:邮件发送失败,将在5秒钟后重试。。。 mailComp:正在重试。。。 发送成功! mailComp:行尾 现在,我放

我不希望用户等待页面完成发送过程,因此我考虑在ASP.NET3.5中使用SendAsync。但是在调试之后,我发现主线程仍然在等待

主要功能:呼叫发送电子邮件功能。。。 mailSend:正在配置。。。。 mailSend:设置不正确的端口。。。。 mailSend:现在尝试发送。。。。 mailSend:行尾 主要功能:电子邮件功能调用完成。 主:过程完成! mailComp:邮件发送失败,将在5秒钟后重试。。。 mailComp:正在重试。。。 发送成功! mailComp:行尾 现在,我放置了错误的端口设置,因此第一封电子邮件失败,并测试第二次是否成功。即使端口正确,页面仍会等待。只有在mailComp函数完成后,页面才会最终发布。这对SendAsyn有限制吗

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

protected void btnReset_Click(object sender, EventArgs e) { try { DataContext db = new DataContext(); var query = from u in db.Fish where u.Username == txtUsername.Text & u.Email == txtEmail.Text select new { u.Username, u.Email }; if (query.Count() != 0) { User user = new User(); String token = user.requestPasswordReset(txtUsername.Text); String URL = Request.Url.AbsoluteUri.ToString() + "?token=" + token; String body = "Reseting you password if you \n" + URL + "\n \n "; Untilty.SendEmail(txtEmail.Text, "Reseting Password", body); litTitle.Text = "Message was sent!"; litInfo.Text = "Please check your email in a few minuets for further instruction."; viewMode(false, false); } else { litCannotFindUserWithEmail.Visible = true; } } catch (Exception ex) { Debug.Write("Main: Exception: " + ex.ToString()); litInfo.Text = "We are currently having some technically difficulty. Please try again in a few minuets. If you are still having issue call us at 905344525"; } } /// public static class Utility /// public static void SendEmail(string recipient, string subject, string body) { MailMessage message = new MailMessage(); message.To.Add(new MailAddress(recipient)); message.From = new MailAddress(fromaddress, "Basadur Profile Website"); message.Subject = subject; message.Body = body; Send(message); } private static void Send(MailMessage msg) { SmtpClient smtp = new SmtpClient(); smtp.Host = "smtp.gmail.com"; smtp.Credentials = new System.Net.NetworkCredential(fromaddress, mailpassword); smtp.EnableSsl = true; Debug.WriteLine("mailSend: setting up incorrect port...."); smtp.Port = 5872; //incorrect port smtp.SendCompleted += new SendCompletedEventHandler(smtp_SendCompleted); smtp.SendAsync(msg, msg); } static void smtp_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) { var msg = (MailMessage)e.UserState; if (e.Error != null) { System.Threading.Thread.Sleep(1000 * 5); try { SmtpClient smtp = new SmtpClient(); smtp.Host = "smtp.gmail.com"; smtp.Credentials = new System.Net.NetworkCredential(fromaddress, mailpassword); smtp.EnableSsl = true; smtp.Port = 587; smtp.Send(msg); } catch (Exception ex) { Debug.WriteLine("mailComp: Failed for the second time giving up."); } } } 受保护的无效btnReset_单击(对象发送者,事件参数e) { 尝试 { DataContext db=新的DataContext(); var query=来自数据库中的u.Fish 其中u.Username==txtexername.Text&u.Email==txtEmail.Text 选择新的{u.用户名,u.电子邮件}; 如果(query.Count()!=0) { 用户=新用户(); 字符串令牌=user.requestPasswordReset(txtUsername.Text); 字符串URL=Request.URL.AbsoluteUri.ToString()+“?token=“+token; String body=“如果您\n”+URL+“\n\n”,则重置密码; Untilty.sendmail(txtEmail.Text,“重置密码”,正文); litTitle.Text=“消息已发送!”; litInfo.Text=“请在几分钟后查看您的电子邮件以了解进一步的说明。”; viewMode(假,假); } 其他的 { litCannotFindUserWithEmail.Visible=true; } } 捕获(例外情况除外) { Debug.Write(“Main:Exception:+ex.ToString()); litInfo.Text=“我们目前在技术上遇到一些困难。请在几分钟后重试。如果仍然存在问题,请致电905344525与我们联系”; } } /// 公共静态类实用程序 /// 公共静态void sendmail(字符串收件人、字符串主题、字符串正文) { MailMessage=新的MailMessage(); message.To.Add(新邮件地址(收件人)); message.From=新邮件地址(fromaddress,“Basadur个人资料网站”); message.Subject=Subject; message.Body=Body; 发送(消息); } 专用静态无效发送(MailMessage msg) { SmtpClient smtp=新SmtpClient(); smtp.Host=“smtp.gmail.com”; smtp.Credentials=新系统.Net.NetworkCredential(fromaddress,mailpassword); smtp.EnableSsl=true; Debug.WriteLine(“mailSend:设置不正确的端口…”); smtp.Port=5872;//端口不正确 smtp.SendCompleted+=新的SendCompletedEventHandler(smtp\u SendCompleted); smtp.SendAsync(msg,msg); } 静态无效smtp_SendCompleted(对象发送方,System.ComponentModel.AsyncCompletedEventArgs e) { var msg=(MailMessage)e.UserState; 如果(例如错误!=null) { 系统线程线程睡眠(1000*5); 尝试 { SmtpClient smtp=新SmtpClient(); smtp.Host=“smtp.gmail.com”; smtp.Credentials=新系统.Net.NetworkCredential(fromaddress,mailpassword); smtp.EnableSsl=true; smtp.Port=587; smtp.Send(msg); } 捕获(例外情况除外) { WriteLine(“mailComp:第二次放弃失败。”); } } } 使用线程:

// 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();

是源代码,它有一个完整的线程教程。

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


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

在.NET 4.5.2中,添加了一种方法,用于在后台调度任务,与任何请求无关:

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

文件中的备注说明:

与普通线程池工作项的不同之处在于ASP.NET可以保留 跟踪当前通过此API注册的工作项的数量 正在运行,ASP.NET运行时将尝试延迟AppDomain关闭 直到这些工作项完成执行

也就是说,你可以用一种“一劳永逸”的方式来完成一项任务,更确信当应用程序域回收时,任务不会被终止

用法如下:

HostingEnvironment.QueueBackgroundWorkItem(cancellationToken =>
{
    try 
    {
       // do work....
    }
    catch(Exception)
    {
        //make sure nothing can throw in here
    }
});

使用下面的代码,您可以使用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);
                });

现在,您可以使用任务将工作分派到线程池。只用

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

您希望“主”执行在哪里恢复?它不能在SendAsync调用之前重新启动。我在想,当我使用SendAsync时,主执行将继续,并设置一个标签,写上“Message send”,然后发布给用户。然后用户将重新获得控制权,即使发送进程仍在服务器的后台工作。我的调试告诉我,这完全是异步的,但是Main仍然在发布之前等待。不同的主题,你绝对不应该在ebtnreset\u Click中做这种工作。您应该从表示层传递到业务服务层。此外,除了业务不可知的数据层之外,您不应该在任何地方直接使用EF上下文。在表示层中使用EF上下文是存在的最糟糕的反模式之一。请尝试将您的同步
Task.Run(()=>  Untilty.SendEmail(txtEmail.Text, "Reseting Password", body));