2011-08-04 36 views
23

我发送一个简单的电子邮件操作:SmtpClient.SendAsync挡住了我的ASP.NET MVC请求

[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。

回答

27

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

如果你想要做真火&忘记,你需要打包你的电话ThreadPool.QueueUserWorkItem。这将强制它在一个新的线程池线程上运行,而不通过SynchronizationContext,所以请求会很高兴地返回。

但是,请注意,如果出于任何原因应用程序域在发送仍在进行中时发生故障(例如,如果您更改了web.config文件,将新文件放入bin中,应用程序池被回收等等)你的异步发送会突然中断。如果你关心的话,看看菲尔哈克斯WebBackgrounderASP.NET,让你排队和运行后台工作(如发送电子邮件),以确保它适度地完成的情况下,应用程序域关闭。

+2

如果我使用Task.Factory.StartNew(()=> SendEmail(),TaskCreationOptions.LongRunning)我会有同样的问题? –

+0

[我应该永远不会调用HostingEnvironment.UnregisterObject?](http://stackoverflow.com/questions/16096378/should-i-never-call-hostingenvironment-unregisterobject) – horgh

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

这可能也成为:

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

我送错误给微软连接,也许你可以通过投票的帮助,然后按一下“我可以重现太” HTTPS ://connect.microsoft.com/VisualStudio/feedback/details/688210/smtpclient-sendasync-blocking-my-asp-net-mvc-request –

+1

'ThreadPool.QueueUserWorkItem'解决了我的问题。谢谢 –

0
+0

我不知道这是否合适,但我一直在使用SendAsync阻止我的MVC 3请求。也许我没有正确配置代码?谷歌搜索,我遇到杰夫Widmer的博客,他演示了异步发送。他的例子是一个很好的起点,对我来说,比驯服SendAsync更简单。 http://weblogs.asp.net/jeffwids/archive/2009/10/12/asynchronously-sending-a-system-net-mail-mailmessage-in-c.aspx?CommentPosted=true#commentmessage – Arnold

+0

他在做什么是做Async的通用方法。关键是,为什么SendAsync方法会阻止请求?框架中的某些东西很尴尬 –

+1

我同意,至少在我的框架中,一定是错误的。但是,我没有看到这个问题。电子邮件确实发送,但我的控制器操作中的重定向被忽略。我还没有看到SendAsync()的完整示例。使用通用方法并强制指定WaitOne()和Dispose()不会导致任何问题。如果我有SendAsync()的配置问题,我不会感到惊讶。谢谢。 – Arnold

1

With .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