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

にバグを送りました。 ASP.NETは、非同期作業が開始された場合に、未処理の非同期作業が完了するまで自動的に終了します。SynchronizationContext。これは、非同期操作がHttpContext,HttpResponseなどとやりとりしようとした場合でも、それがまだ周囲にあることを保証するためです。

火災&を忘れた場合は、ThreadPool.QueueUserWorkItemに電話をかける必要があります。これにより、SynchronizationContextを経由せずに新しいスレッドプールスレッド上で強制的に実行されるため、リクエストがうまく戻ってきます。

ただし、あなたの送信がまだ進行中の場合(たとえば、web.configファイルを変更した場合、新しいファイルをbinにドロップした場合、アプリケーションプールをリサイクルした場合など)、アプリドメインがダウンした場合あなたの非同期送信は突然中断されます。それを気にする人はWebBackgrounderのASP.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にバグを送りました://connect.microsoft.com/VisualStudio/feedback/details/688210/smtpclient-sendasync-blocking-my-asp-net-mvc-request –

+1

'ThreadPool.QueueUserWorkItem'は私の問題を解決します。ありがとう –

0
+0

私はこれがapropoかどうかわかりませんが、私もSendAsyncでMVC 3リクエストをブロックしている問題がありました。コードを正しく設定していない可能性がありますか?グーグルで、私はJeff WidmerのBlogを訪れ、Sendを非同期に呼び出す方法を示しています。彼の例は素晴らしい出発点です。私にとっては、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

.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

関連する問題