SmtpClient.SendAsync阻止我的ASP.NET MVC请求

Fel*_*oto 23 email smtp smtpclient asp.net-mvc-3

我有一个发送简单电子邮件的动作:

    [HttpPost, ActionName("Index")]
    public ActionResult IndexPost(ContactForm contactForm)
    {
        if (ModelState.IsValid)
        {
            new EmailService().SendAsync(contactForm.Email, contactForm.Name, contactForm.Subject, contactForm.Body, true);

            return RedirectToAction(MVC.Contact.Success());
        }
        return View(contactForm);
    }
Run Code Online (Sandbox Code Playgroud)

和电子邮件服务:

    public void SendAsync(string fromEmail, string fromName, string subject, string body, bool isBodyHtml)
    {
        MailMessage mailMessage....
        ....
        SmtpClient client = new SmtpClient(settingRepository.SmtpAddress, settingRepository.SmtpPort);

        client.EnableSsl = settingRepository.SmtpSsl;
        client.Credentials = new NetworkCredential(settingRepository.SmtpUserName, settingRepository.SmtpPassword);
        client.SendCompleted += client_SendCompleted;
        client.SendAsync(mailMessage, Tuple.Create(client, mailMessage));
    }

    private void client_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    {
        Tuple<SmtpClient, MailMessage> data = (Tuple<SmtpClient, MailMessage>)e.UserState;
        data.Item1.Dispose();
        data.Item2.Dispose();

        if (e.Error != null)
        {

        }
    }
Run Code Online (Sandbox Code Playgroud)

当我发送电子邮件时,我使用的是Async方法,然后我的方法SendAsync立即返回,然后调用RedirectToAction.但是,在client_SendCompleted完成之前,ASP.NET不会发送响应(在这种情况下是重定向).

这是我想要了解的内容:

在Visual Studio调试器中观察执行时,SendAsync立即返回(并调用RedirectToAction),但在发送电子邮件之前,浏览器中没有任何反应?

如果我在client_SendCompleted中放置一个断点,客户端将继续加载....直到我在调试器中点击F5.

Dam*_*rds 27

这是设计的.如果异步工作以调用底层SynchronizationContext的方式启动,ASP.NET将在完成请求之前自动等待任何未完成的异步工作完成.这是为了确保如果您的异步操作尝试与HttpContext,HttpResponse等进行交互,它仍将存在.

如果你想做真假和忘记,你需要打电话ThreadPool.QueueUserWorkItem.这将强制它在新的线程池线程上运行而无需通过SynchronizationContext,因此请求将很高兴地返回.

但是请注意,如果出于任何原因,当您的发送仍在进行时,应用程序域将停止运行(例如,如果您更改了web.config文件,将新文件放入bin,应用程序池已回收,等等)您的异步发送会突然中断.如果您关心这一点,请查看用于ASP.NET的 Phil Haacks WebBackgrounder,让您排队并运行后台工作(如发送电子邮件),以确保在app域关闭的情况下优雅地完成.

  • 如果我使用Task.Factory.StartNew(()=> SendEmail(),TaskCreationOptions.LongRunning)我会遇到同样的问题吗? (2认同)

The*_*ing 6

这是一个有趣的.我重现了意想不到的行为,但我无法解释.我会继续挖掘.

无论如何,解决方案似乎是将后台线程排队,这种方式在使用中失败了SendAsync.你最终得到这个:

MailMessage mailMessage = new MailMessage(...);
SmtpClient client = new SmtpClient(...);
client.SendCompleted += (s, e) =>
                            {
                                client.Dispose();
                                mailMessage.Dispose();
                            };

ThreadPool.QueueUserWorkItem(o => 
    client.SendAsync(mailMessage, Tuple.Create(client, mailMessage))); 
Run Code Online (Sandbox Code Playgroud)

哪个可能成为:

ThreadPool.QueueUserWorkItem(o => {
    using (SmtpClient client = new SmtpClient(...))
    {
        using (MailMessage mailMessage = new MailMessage(...))
        {
            client.Send(mailMessage, Tuple.Create(client, mailMessage));
        }
    }
}); 
Run Code Online (Sandbox Code Playgroud)