2012-08-30 102 views
10

我正在使用ASP.NET MVC 3與MVCMailer,我試圖使用SendAsync發送電子郵件,但實際上它仍然需要更長的時間。發送異步電子郵件

所以我試圖用Task.Factory像波紋管代碼:

var task1 = Task.Factory.StartNew(
      state => 
      { 
       var mail = new UserMailer(); 
       var msg = mail.Welcome("My Name", "[email protected]"); 
       msg.SendAsync(); 
      }); 

    task1.Wait(); 

的問題是,MVCMailer需要HttpContext的,但是這裏面的任務我的HttpContext空。

如何發送異步電子郵件?

回答

18

一個小除了這一點。這裏有一個擴展方法來幫助一些。

using Mvc.Mailer; 
using System.Threading.Tasks; 
public static void SendEmailAsync(this MvcMailMessage msg, HttpContext currContext) 
{ 
     //make this process a little cleaner 
     Task.Factory.StartNew(() => 
     { 
      System.Web.HttpContext.Current = currContext; 
      msg.SendAsync(); 
     }); 
} 

從你的控制器方法使用它如下所示。

Mailers.UserMailer um = new Mailers.UserMailer(); 
um.SendWelcomeEmail(dataObject).SendEmailAsync(ControllerContext.HttpContext.ApplicationInstance.Context); 
+0

Hey Matt!感謝您的參考,這看起來不錯,它是否異步發送100%,我聽說有人說MVCMailer是僞異步哈哈。謝了哥們! – Baconbeastnz

+2

沒有問題!我在這張桌子上撞了一下桌子:) – Matt

+0

@ Matt,它很棒!謝謝!!! =) –

0

你不需要任務。 SendAsync是異步的並使用另一個線程自我。任務不會加速你郵寄。

UPDATE: 當我解決同樣的問題,我使用任務和同步發送。似乎SendAsync並非如此異常。這是我的代碼示例(這是不希望的HttpContext):

public void SendMailCollection(IEnumerable<Tuple<string, string, MailAddress>> mailParams) 
    { 
     var smtpClient = new SmtpClient 
     { 
      Credentials = new NetworkCredential(_configurationService.SmtpUser, _configurationService.SmtpPassword), 
      Host = _configurationService.SmtpHost, 
      Port = _configurationService.SmtpPort.Value 
     }; 

     var task = new Task(() => 
           { 
            foreach (MailMessage message in mailParams.Select(FormMessage)) 
            { 
             smtpClient.Send(message); 
            } 

           }); 
     task.Start(); 
    } 

    private MailMessage FormMessage(Tuple<string, string, MailAddress> firstMail) 
    { 
     var message = new MailMessage 
      { 
       From = new MailAddress(_configurationService.SmtpSenderEmail, _configurationService.SmtpSenderName), 
       Subject = firstMail.Item1, 
       Body = firstMail.Item2 
      }; 
     message.To.Add(firstMail.Item3); 
     return message; 
    } 
+0

基里爾,爲什麼需要更長的時間?我需要在後臺發送電子郵件。 –

+0

異步並不意味着快。這意味着你的緩慢進程不會阻止你的基線程,並且你的應用程序會對用戶動作做出反應。 –

+0

當然,但是當我發送電子郵件時,進程被這個任務阻塞。 –

4

Task.Factory.StartNew將創建一個新的線程。
如果您要訪問HttpContext的這是在主線程,你必須這樣做:

var task1 = Task.Factory.StartNew(() => 
    { 
    System.Web.HttpContext.Current = ControllerContext.HttpContext.ApplicationInstance.Context; 
    var mail = new UserMailer(); 
    var msg = mail.Welcome("My Name", "[email protected]"); 
    msg.SendAsync(); 
    }); 

task1.Wait(); 

有一個長時間的辯論,如果它是更好地使用TPL或QueueUserWorkItem。
有人試圖解決issue
這是QueueUserWorkItem版本:

public class HomeController : Controller 
{ 
    private AutoResetEvent s_reset = new AutoResetEvent(false); 

    public ActionResult Index() 
    { 
     var state = new WorkerState() { HttpContextReference = System.Web.HttpContext.Current }; 
     ThreadPool.QueueUserWorkItem(new WaitCallback(EmaiSenderWorker), state); 

     try 
     { 
     s_reset.WaitOne(); 
     } 
     finally 
     { 
     s_reset.Close(); 
     } 

     return View(); 
    } 

    void EmaiSenderWorker(object state) 
    { 
     var mystate = state as WorkerState; 

     if (mystate != null && mystate.HttpContextReference != null) 
     { 
     System.Web.HttpContext.Current = mystate.HttpContextReference; 
     } 

     var mail = new UserMailer(); 
     var msg = mail.Welcome(); 
     msg.SendAsync(); 

     s_reset.Set(); 
    } 

    private class WorkerState 
    { 
     public HttpContext HttpContextReference { get; set; } 
    } 

} 
+0

是的LeflyX!我已經試過了,但我不知道是否可以將上下文變量傳遞給MVCMailer對象。 –

+1

@MichelAndrade:我已經更新了我的答案。試着看看它是否有幫助。 – LeftyX

+0

LeflyX,幾乎在那裏,如果我脫掉「task1.Wait();」它不起作用 –

0
public class UserMailer : MailerBase  
{ 
    RegisterService Service = new RegisterService(); 
    HttpContext Context; 
    public UserMailer(HttpContext context) 
    { 
     Context = context; 
     MasterName="_Layout"; 
    } 

    public void ConfirmRegistration(Register model) 
    { 
     SendAsync(() => 
     { 
      model.Conference = Service.Context.Conferences.Find(model.ConferenceID); // load conference bcs it fails to lazy load automatically. 
      ViewData.Model = model; 
      return Populate(x => 
      { 
       x.Subject = "You registered for " + model.Conference.Name + "!"; ; 
       x.ViewName = "Confirm"; 
       x.To.Add(model.Email); 
      }); 
     }); 
    } 

    private void SendAsync(Func<MvcMailMessage> GetEmail) 
    { 
     Task.Factory.StartNew(() => 
     { 
      System.Web.HttpContext.Current = Context; 
      GetEmail().Send(); 
     }); 
    } 
+2

感謝您發佈答案!雖然代碼片段可以回答這個問題,但仍然非常適合添加一些附加信息,例如解釋等。 – j0k