2011-08-04 53 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