2011-08-04 34 views
23

Tôi có một hành động mà sẽ gửi một email đơn giản:SmtpClient.SendAsync chặn tôi ASP.NET MVC Yêu cầu

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

Và một dịch vụ thư điện tử:

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

     } 
    } 

Khi tôi gửi một email, tôi sử dụng phương thức Async, sau đó phương thức SendAsync của tôi trả về ngay lập tức, sau đó RedirectToAction được gọi. Nhưng phản hồi (trong trường hợp này là một chuyển hướng) không được gửi bởi ASP.NET cho đến khi client_SendCompleted được hoàn tất.

Dưới đây là những gì tôi đang cố gắng để hiểu:

Khi xem việc thực hiện trong Visual Studio debugger, các SendAsync trả về ngay lập tức (và RedirectToAction được gọi), nhưng không có gì xảy ra trong trình duyệt cho đến khi email được gửi?

Nếu tôi đặt một điểm ngắt bên trong client_SendCompleted, khách hàng ở tại tải .... cho đến khi tôi nhấn F5 tại trình gỡ lỗi.

Trả lời

27

này là do thiết kế. ASP.NET sẽ tự động chờ cho bất kỳ công việc không đồng bộ xuất sắc nào kết thúc trước khi hoàn thành yêu cầu nếu công việc async được khởi động theo cách gọi vào số Đồng bộ hóa tiếp theo. Điều này nhằm đảm bảo rằng nếu hoạt động không đồng bộ của bạn cố gắng tương tác với HttpContext, HttpResponse, v.v., nó sẽ vẫn còn xung quanh.

Nếu bạn muốn thực hiện lửa & quên, bạn cần phải kết thúc cuộc gọi của mình theo số ThreadPool.QueueUserWorkItem. Điều này sẽ buộc nó chạy trên một chủ đề hồ bơi thread mới mà không cần thông qua SynchronizationContext, do đó, yêu cầu sau đó sẽ trở lại hạnh phúc. Tuy nhiên, nếu bạn thay đổi tệp web.config, hãy bỏ một tệp mới vào thùng rác, nhóm ứng dụng đã được tái chế, v.v ... nếu bạn vì lý do nào đó miền ứng dụng bị giảm xuống. .) gửi không đồng bộ của bạn sẽ bị gián đoạn đột ngột. Nếu bạn quan tâm đến điều đó, hãy xem Phil Haacks WebBackgrounder cho ASP.NET, cho phép bạn xếp hàng và chạy công việc nền (như gửi email) theo cách như vậy sẽ đảm bảo nó hoàn thành một cách duyên dáng trong trường hợp miền ứng dụng tắt.

+2

nếu tôi sử dụng Task.Factory.StartNew (() => SendEmail(), TaskCreationOptions.LongRunning) Tôi sẽ có cùng một vấn đề? –

+0

[Tôi có nên không bao giờ gọi HostingEnvironment.UnregisterObject?] (Http://stackoverflow.com/questions/16096378/should-i-never-call-hostingenvironment-unregisterobject) – horgh

6

Đây là một điều thú vị. Tôi đã sao chép hành vi bất ngờ, nhưng tôi không thể giải thích được. Tôi sẽ tiếp tục đào bới.

Dù sao giải pháp có vẻ là xếp hàng một chuỗi nền, loại đánh bại mục đích sử dụng SendAsync. Bạn kết thúc với điều này:

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

nào cũng có thể trở thành:

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

tôi đã gửi lỗi cho Microsoft Connect, có thể bạn có thể giúp đỡ bằng cách bỏ phiếu và nhấp vào "Tôi có thể tái tạo quá" https : //connect.microsoft.com/VisualStudio/feedback/details/688210/smtpclient-sendasync-blocking-my-asp-net-mvc-request –

+1

'ThreadPool.QueueUserWorkItem' giải quyết vấn đề của tôi. Cảm ơn –

0
+0

Tôi không biết nếu điều này là apropo, nhưng tôi đã có vấn đề với SendAsync chặn yêu cầu MVC 3 của tôi, quá. Có lẽ tôi không cấu hình mã một cách chính xác? Googling, tôi đã xem Blog của Jeff Widmer, nơi anh ấy chứng minh việc gọi Gửi không đồng bộ. Ví dụ của ông là một điểm khởi đầu tuyệt vời và, đối với tôi, đơn giản hơn là đấu vật với 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

Những gì anh ấy đang làm là một cách tiếp cận chung để làm Async. Vấn đề là tại sao phương thức SendAsync chặn yêu cầu? Một cái gì đó trong khuôn khổ là vụng về –

+1

Tôi đồng ý, một cái gì đó phải không ổn, ít nhất là trong khuôn khổ của tôi. Tuy nhiên, tôi không thấy vấn đề. Email không được gửi, nhưng việc chuyển hướng trong Hành động điều khiển của tôi bị bỏ qua. Tôi chưa thấy một ví dụ hoàn chỉnh về SendAsync(). Sử dụng một cách tiếp cận chung và quy định cụ thể WaitOne() và Dispose() không gây ra vấn đề gì. Tôi sẽ không ngạc nhiên nếu tôi có vấn đề về cấu hình với SendAsync(). Cảm ơn. – Arnold

1

Với .Net 4.5.2, bạn có thể làm điều này với 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); 
     }); 

Xin vui lòng đọc này đầu tiên: http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx

Các vấn đề liên quan