2012-08-30 4 views
10

Sto usando ASP.NET MVC 3 con MVCMailer, ho provato a inviare e-mail usando SendAsync, ma in realtà ci vuole ancora più tempo.Invia e-mail asincrone

Così sto cercando di usare Task.Factory come il codice sotto:

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

    task1.Wait(); 

Il problema è, MVCMailer ha bisogno di HttpContext, ma all'interno di questo compito ho avuto HttpContext Null.

Come posso inviare e-mail asincrone?

risposta

18

Una piccola aggiunta a questo. Ecco un metodo di estensione per aiutare alcuni.

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

utilizzarlo come segue dai vostri metodi del controller.

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

Hey Matt! grazie per il riferimento, questo sembra fantastico, lo invia in modo asincrono al 100%, ho sentito dire che MVCMailer era pseudo async haha. Grazie compagno! – Baconbeastnz

+2

Nessun problema! Stavo sbattendo la testa contro la scrivania per un po 'con questo :) – Matt

+0

@ Matt, funziona benissimo !!! Grazie!!! =) –

0

Non hai bisogno di attività. SendAsync è asincrono e utilizza un altro thread autonomo. Le attività non accelerano la spedizione.

UPDATE: Quando risolvo lo stesso problema, utilizzo attività e invio sincrono. Sembra che SendAsync non fosse così asincrono. Si tratta di esempio del mio codice (non si vuole 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, perché occorre più tempo? Ho bisogno di inviare e-mail in background. –

+0

Asincrono non significa molto veloce. Significa che il tuo processo lento non bloccherà il tuo thread di base e la tua applicazione reagirà alle azioni dell'utente –

+0

Certo, ma quando invio email, il processo è bloccato da questa attività. –

4

Task.Factory.StartNew creerà un nuovo thread.
Se si desidera accedere HttpContext che è nel thread principale che devi fare questo:

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

C'è un lungo dibattito se è meglio usare TPL o QueueUserWorkItem.
Qualcuno ha provato ad indirizzare lo issue.
Questa è la versione 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! Ho già provato questo, ma non so se è possibile passare la variabile di contesto all'oggetto MVCMailer. –

+1

@MichelAndrade: ho aggiornato la mia risposta. Prova a vedere se aiuta. – LeftyX

+0

LeflyX, quasi lì, se decollo "task1.Wait();" non funziona –

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

Grazie per aver postato una risposta! Mentre un frammento di codice potrebbe rispondere alla domanda è comunque bello aggiungere alcune informazioni aggiuntive in giro, come spiegare, ecc. – j0k