2012-08-30 101 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