2012-01-07 15 views
8

Concepto simple aquí. Esto es para un sitio que se está construyendo utilizando MVC 3 y Entity Framework 4. Después de que un usuario se registra en el sitio, se envía un correo electrónico a su dirección de correo electrónico. Primero lo implementé usando SmtpClient.Send() y funcionó bien. Luego tuve la brillante idea de intentar enviar el correo electrónico de forma asíncrona. Tengo problemas con los dos enfoques asíncronos que he probado.Dos formas de enviar correo electrónico a través de SmtpClient de forma asíncrona, resultados diferentes

primera implementación (de este post sin respuesta: 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); 
    } 
} 

Esto funcionaba la mitad del tiempo. La otra mitad recibiría un error "No se puede acceder a un objeto eliminado" en la devolución de llamada.

siguiente aplicación (de un miembro de la reputación sólida: 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 esta aplicación no consigo ningún error, pero hay un retraso notablemente más largo (~ 5 segundos) en el modo de depuración cuando SMTPClient. SendAsync() se ejecuta, lo que me lleva a pensar que no se envía de forma asincrónica.

Preguntas:

1) lo que está mal en el primer método que está causando los errores "objeto desechado"?

2) ¿La segunda implementación tiene un problema que causa que el correo electrónico no se envíe de forma asíncrona? ¿Es la demora de 5 segundos sin sentido?

También podría ser importante tener en cuenta que aunque el sitio no necesitará admitir el envío de un gran número de correos electrónicos (solo registro de usuarios, boletines opcionales, etc.), anticipamos una gran cantidad de usuarios en el futuro, de ahí mi decisión de enviar correos electrónicos de forma asincrónica.

Gracias.

Respuesta

7

Su primer método no funcionará correctamente debido al bloque USING. Después de que finaliza el uso del bloque, el objeto SmtpClient será eliminado. Por lo tanto, no puede tener acceso a él en su controlador de eventos.

+0

Gracias por la respuesta. Ahora veo que el objeto está dispuesto antes de que el controlador tenga la oportunidad de disparar. Además, entiendo que tanto SmtpClient se debe eliminar como que se debe invocar a EndInvoke. ¿El acercamiento # 2 logra esto? –

5

consejos: uso 1-dont "usando el bloque" para objet MailMessage, que disponen de su objeto antes de que el correo enviado
2-desechar objetos MailMessage en SmtpClient.SendCompleted evento:

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

3-set SendCompletedEventHandler para el objeto SmtpClient

smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback); 

4-más código:

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 es el método preferido de envío de correo electrónico asincrónico ya que utiliza los métodos SmtpClient especialmente diseñados para este fin. También es más fácil de implementar y se ha demostrado que funciona miles de veces.

Su retraso de 5 segundos es extraño y sugiere que hay un problema que debe abordarse. La primera parte del código es simplemente cubrir el problema, pero no lo elimina.

SmtpClient.SendAsync en realidad sólo se envían de forma asíncrona, si el método de entrega es noSpecifiedPickupDirectory o PickupDirectoryFromIis. En esos casos, escribirá el archivo de mensaje en la carpeta de recogida antes de volver. Verifique la sección <smtp> de su archivo de configuración. Supongo que estás usando uno de estos métodos y el problema está en la carpeta de recolección. Borre los archivos viejos que pueda tener allí y verifique si el problema no es su software antivirus que muy probablemente busca virus en cada nuevo archivo. Compruebe si hay atributos de cifrado o compresión establecidos. Puede ser algo más también La mejor forma de comprobar si la carpeta es el origen de los problemas es al hacer frente manualmente a un archivo de correo electrónico.

Cuestiones relacionadas