2012-01-07 5 views
8

Semplice concetto qui. Questo è per un sito che viene creato utilizzando MVC 3 e Entity Framework 4. Dopo che un utente si registra sul sito, viene inviata un'email al suo indirizzo email. L'ho implementato per la prima volta utilizzando SmtpClient.Send() e ha funzionato correttamente. Poi ho avuto la brillante idea di provare a inviare l'e-mail in modo asincrono. Sto riscontrando problemi con i due approcci asincroni che ho provato.Due modi per inviare e-mail tramite SmtpClient in modo asincrono, diversi risultati

Prima implementazione (da questo post senza risposta: https://stackoverflow.com/questions/7558582/how-to-dispose-using-smtpclient-send-and-asynccallback):

public bool Emailer(){ 
    . 
    . 
    . 
    using (var smtpClient = new SmtpClient()) 
    { 
     smtpClient.EnableSsl = true; 
     smtpClient.Host = "smtp.gmail.com"; 
     smtpClient.Port = 587; 
     smtpClient.UseDefaultCredentials = false; 
     smtpClient.Credentials = new NetworkCredential("[email protected]", "mypassword"); 

     var sd = new SendEmailDelegate(smtpClient.Send); 
     var cb = new AsyncCallback(SendEmailResponse); 
     sd.BeginInvoke(message, cb, sd); 

     return true; 
    } 
} 

private delegate void SendEmailDelegate(System.Net.Mail.MailMessage m); 

private static void SendEmailResponse(IAsyncResult ar) 
{ 
    try 
    { 
     SendEmailDelegate sd = (SendEmailDelegate)(ar.AsyncState); 
     sd.EndInvoke(ar); // "cannot access a disposed object" errors here 
    } 
    catch (Exception e) 
    { 
     _logger.WarnException("Error on EndInvoke.", e); 
    } 
} 

Questo ha funzionato la metà del tempo. L'altra metà otterrebbe un errore "Impossibile accedere a un oggetto disposto" nel CallBack.

implementazione successiva (da un membro con solida reputazione: What are best practices for using SmtpClient, SendAsync and Dispose under .NET 4.0):

var smtpClient = new SmtpClient(); 
smtpClient.EnableSsl = true; 
smtpClient.Host = "smtp.gmail.com"; 
smtpClient.Port = 587; 
smtpClient.UseDefaultCredentials = false; 
smtpClient.Credentials = new NetworkCredential("[email protected]", "mypassword"); 

smtpClient.SendCompleted += (s, e) => 
    { 
     smtpClient.Dispose(); 
     message.Dispose(); 
    }; 
smtpClient.SendAsync(message, null); 

Con questa implementazione non ottengo eventuali errori, ma c'è un ritardo notevolmente più lungo (~ 5 secondi) in modalità debug quando SMTPClient. SendAsync() viene eseguito, portandomi a pensare che non venga inviato in modo asincrono.

Domande:

1) cosa c'è di sbagliato nel primo metodo che sta causando gli errori "oggetto eliminato"?

2) la seconda implementazione ha un problema che causa il mancato invio dell'email in modo asincrono? Il ritardo di 5 secondi è privo di significato?

Potrebbe anche essere importante notare che, sebbene il sito non debba supportare l'invio di un gran numero di e-mail (solo registrazione utente, newsletter opt-in, ecc.), Prevediamo un numero elevato di utenti in futuro, da qui la mia decisione di inviare e-mail in modo asincrono.

Grazie.

risposta

7

Il metodo del pugno non funziona correttamente a causa del blocco USING. Al termine del blocco using, l'oggetto SmtpClient verrà eliminato. Quindi non puoi accedervi nel tuo gestore di eventi.

+0

Grazie per la risposta. Ora vedo che l'oggetto è disposto prima che il gestore abbia la possibilità di sparare. Inoltre, comprendo che entrambi SmtpClient devono essere eliminati e EndInvoke deve essere chiamato. L'approccio n. 2 porta a termine questo? –

5

consigli: uso 1-dont "utilizzando blocco" per MailMessage objet, è smaltire l'oggetto prima di posta elettronica inviato
2-smaltire oggetti MailMessage su SmtpClient.SendCompleted evento:

smtpClient.SendCompleted += (s, e) => 
    { 
     message.Dispose(); 
    }; 

3-set SendCompletedEventHandler per oggetto smtpclient

smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback); 

4-più codice:

private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e) 
    { 
     // Get the unique identifier for this asynchronous operation. 
     String token = (string)e.UserState; 

     if (e.Cancelled) 
     { 
      //write your code here 
     } 
     if (e.Error != null) 
     { 
      //write your code here 
     } 
     else //mail sent 
     { 
      //write your code here 
     } 

     mailSent = true; 
    } 
1

SmtpClient.SendAsync è il metodo preferito di invio e-mail asincrono poiché utilizza i metodi SmtpClient appositamente progettati per questo scopo. È anche più semplice da implementare e ha dimostrato di funzionare migliaia di volte.

Il ritardo di 5 secondi è strano e suggerisce che c'è un problema che deve essere risolto. Il primo pezzo di codice copre semplicemente il problema ma non lo elimina.

SmtpClient.SendAsync sarà effettivamente inviare solo in modo asincrono se il metodo di consegna è nonSpecifiedPickupDirectory o PickupDirectoryFromIis. In questi casi, scriverà il file del messaggio nella cartella di prelievo prima di tornare. Controlla la sezione <smtp> del file di configurazione. La mia ipotesi è che stai usando uno di questi metodi e il problema è con la cartella di prelievo. Elimina i vecchi file che potresti avere lì e controlla se il problema non è il tuo software antivirus che probabilmente cerca virus per ogni nuovo file. Controlla se sono impostati gli attributi di crittografia o compressione. Può essere anche qualcos'altro. Il modo migliore per verificare se la cartella è la fonte dei problemi consiste nel copiare manualmente un file di posta elettronica in esso.

Problemi correlati