发送异步电子邮件

我在MVCMailer中使用ASP.NET MVC 3,我尝试使用SendAsync发送电子邮件,但实际上它还需要更长时间。

所以我试图像下面的代码一样使用Task.Factory:

var task1 = Task.Factory.StartNew( state => { var mail = new UserMailer(); var msg = mail.Welcome("My Name", "myemail@gmail.com"); msg.SendAsync(); }); task1.Wait(); 

问题是,MVCMailer需要HttpContext,但在这个任务中我得到了HttpContext Null。

如何发送Async电子邮件?

这是一个小小的补充。 这是一个帮助一些的扩展方法。

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

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", "myemail@gmail.com"); msg.SendAsync(); }); task1.Wait(); 

如果使用TPL或QueueUserWorkItem更好,那么会有很长的争论。
有人试图解决这个问题 。
这是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; } } } 

你不需要任务。 SendAsync是异步的,并使用另一个自我线程。 任务不会加速你的邮寄。

更新 :当我解决相同的问题时,我使用任务和同步发送。 似乎SendAsync不是那么异乎寻常。 它是我的代码示例(它不想要HttpContext):

 public void SendMailCollection(IEnumerable> 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 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; } 
 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 GetEmail) { Task.Factory.StartNew(() => { System.Web.HttpContext.Current = Context; GetEmail().Send(); }); }