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 将在完成请求之前自动等待任何未完成的异步工作完成。这是为了确保如果您的异步操作尝试与 HttpContext、HttpResponse 等进行交互,它仍然存在。
如果您想要真正的成功并忘记,您需要将您的电话转入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 请求的主要内容,如果未能解决你的问题,请参考以下文章