2012-08-30 29 views
10

Estoy usando ASP.NET MVC 3 con MVCMailer, traté de enviar correos electrónicos utilizando SendAsync, pero en realidad aún lleva más tiempo.Enviar correos electrónicos asíncronos

así que estoy tratando de utilizar Task.Factory como el código de abajo:

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

    task1.Wait(); 

El problema es, MVCMailer necesita HttpContext, pero dentro de esta tarea que tiene HttpContext nulo.

¿Cómo puedo enviar correos electrónicos Async?

Respuesta

18

una pequeña adición a esto. Aquí hay un método de extensión para ayudar a algunos.

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

Utilícelo como sigue desde los métodos de su controlador.

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

¡Hola, Matt! gracias por la referencia, esto se ve muy bien, lo envía asincrónicamente al 100%, he escuchado a la gente decir que MVCMailer era pseudo asincrónico jaja. ¡Gracias amigo! – Baconbeastnz

+2

¡Sin problema! Estaba golpeando mi cabeza contra el escritorio por un momento :) – Matt

+0

@ Matt, funciona genial !!! ¡¡¡Gracias!!! =) –

0

No necesita tareas. SendAsync es asincrónico y usa otro hilo propio. Las tareas no aceleran su envío por correo.

ACTUALIZACIÓN: Cuando resuelvo mismo problema que yo uso de tareas y enviar síncrona. Parece que SendAsync no era tan asincrónico. Es muestra de mi código (no se quiere 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

Kirill, ¿por qué tarda más? Necesito enviar un correo electrónico en segundo plano. –

+0

Asincrónico no significa rápido. Significa que su proceso lento no bloqueará su subproceso base y su aplicación reaccionará en las acciones del usuario –

+0

Claro, pero cuando envío correos electrónicos, el proceso es bloqueado por esta tarea. –

4

Task.Factory.StartNew va a crear un nuevo hilo.
Si desea acceder a HttpContext que está en el hilo principal que tiene que hacer esto:

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

Hay un largo debate si es mejor utilizar TPL o QueueUserWorkItem.
Alguien ha intentado dirigirse al issue.
Esta es la versión 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

Sí LeflyX! Ya probé esto, pero no sé si es posible pasar la variable de contexto al objeto MVCMailer. –

+1

@MichelAndrade: He actualizado mi respuesta. Intenta ver si ayuda. – LeftyX

+0

LeflyX, Casi allí, si me quito "task1.Wait();" no funciona –

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

¡Gracias por publicar una respuesta! Si bien un fragmento de código podría responder a la pregunta, sigue siendo genial agregar información adicional, como explicar, etc. – j0k

Cuestiones relacionadas