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

Posted

技术标签:

【中文标题】SmtpClient.SendAsync 阻止我的 ASP.NET MVC 请求【英文标题】:SmtpClient.SendAsync blocking my ASP.NET MVC Request 【发布时间】:2011-10-19 14:38:06 【问题描述】:

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

    [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);
    

还有一个电子邮件服务:

    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)
        

        
    

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

这是我想要理解的:

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

如果我在 client_SendCompleted 中设置断点,客户端会一直加载...直到我在调试器中按 F5。

【问题讨论】:

【参考方案1】:

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

如果您想要真正的成功并忘记,您需要将您的电话转入ThreadPool.QueueUserWorkItem。这将强制它在一个新的线程池线程上运行而无需经过 SynchronizationContext,因此请求将愉快地返回。

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

【讨论】:

如果我使用 Task.Factory.StartNew(() => SendEmail(), TaskCreationOptions.LongRunning) 我会遇到同样的问题吗? Should I never call HostingEnvironment.UnregisterObject?【参考方案2】:

这是一个有趣的问题。我已经重现了意外的行为,但我无法解释。我会继续挖掘。

无论如何,解决方案似乎是将后台线程排队,这有悖于使用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))); 

也可能变成:

ThreadPool.QueueUserWorkItem(o => 
    using (SmtpClient client = new SmtpClient(...))
    
        using (MailMessage mailMessage = new MailMessage(...))
        
            client.Send(mailMessage, Tuple.Create(client, mailMessage));
        
    
); 

【讨论】:

我已将错误发送到 Microsoft Connect,也许您可​​以通过投票并点击“我也可以重现”connect.microsoft.com/VisualStudio/feedback/details/688210/… 来提供帮助【参考方案3】:

使用 .Net 4.5.2,您可以使用 ActionMailer.Net

        var mailer = new MailController();
        var msg = mailer.SomeMailAction(recipient);

        var tcs = new TaskCompletionSource<MailMessage>();
        mailer.OnMailSentCallback = tcs.SetResult;
        HostingEnvironment.QueueBackgroundWorkItem(async ct =>
        
            msg.DeliverAsync();
            await tcs.Task;
            Trace.TraceInformation("Mail sent to " + recipient);
        );

请先阅读:http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx

【讨论】:

【参考方案4】:

我将错误发送到 Microsoft Connect https://connect.microsoft.com/VisualStudio/feedback/details/688210/smtpclient-sendasync-blocking-my-asp-net-mvc-request

【讨论】:

我不知道这是否合适,但我也遇到了 SendAsync 阻止我的 MVC 3 请求的问题。也许我没有正确配置代码?谷歌搜索,我遇到了 Jeff Widmer 的博客,他在其中演示了异步调用 Send。他的例子是一个很好的起点,对我来说,比使用 SendAsync 更简单。 weblogs.asp.net/jeffwids/archive/2009/10/12/… 他正在做的是一种通用的异步方法。关键是,为什么 SendAsync 方法会阻止请求?框架中的某些东西很尴尬 我同意,肯定有问题,至少在我的框架中。但是,我没有看到这个问题。确实发送了电子邮件,但我的控制器操作中的重定向被忽略了。我还没有看到 SendAsync() 的完整示例。使用通用方法并强制指定 WaitOne() 和 Dispose() 不会导致任何问题。如果我有 SendAsync() 的配置问题,我不会感到惊讶。谢谢。

以上是关于SmtpClient.SendAsync 阻止我的 ASP.NET MVC 请求的主要内容,如果未能解决你的问题,请参考以下文章

我怎样才能阻止 h2 如此多地阻止我的休眠查询

如何在我的应用程序中阻止用户(swift + parse)

为啥我的防病毒软件阻止我的代码执行...?

如何阻止我的脚本循环

为啥我的 formdata POST 没有被 Cors 政策阻止?

如何阻止我的网站加载到其他网站 iframe