2011-09-01 17 views
86

? Estoy un poco perplejo sobre cómo administrar SmtpClient ahora que es desechable, especialmente si realizo llamadas usando SendAsync. Presumiblemente, no debería llamar a Dispose hasta que SendAsync se complete. Pero debería llamarlo alguna vez (por ejemplo, usar "usar"). El escenario es un servicio WCF que envía correos electrónicos periódicamente cuando se realizan llamadas. La mayor parte del cálculo es rápido, pero el envío de correo electrónico puede tomar un segundo más o menos, por lo que Async sería preferible.¿Cuáles son las mejores prácticas para usar SmtpClient, SendAsync y Dispose en .NET 4.0

¿Debo crear un nuevo SmtpClient cada vez que envío un correo? ¿Debo crear uno para toda la WCF? ¡Ayuda!

Actualización En caso de que haga una diferencia, cada correo electrónico siempre se personaliza para el usuario. El WCF está alojado en Azure y Gmail se utiliza como el anuncio publicitario.

+1

Ver este post sobre el panorama general sobre cómo manejar IDisposable y asincrónico: http://stackoverflow.com/questions/974945/how-to-dispose-objects-having-asynchronous-methods -llamado –

Respuesta

107

Nota: .NET 4.5 SmtpClient implementa async awaitable método SendMailAsync. Para versiones inferiores, use SendAsync como se describe a continuación.


siempre se debe disponer de IDisposable casos con la mayor brevedad. En el caso de las llamadas asincrónicas, esto se realiza en la devolución de llamada después de enviar el mensaje.

var message = new MailMessage("from", "to", "subject", "body")) 
var client = new SmtpClient("host"); 
client.SendCompleted += (s, e) => { 
          client.Dispose(); 
          message.Dispose(); 
         }; 
client.SendAsync(message, null); 

Es un poco molesto el SendAsync no acepta una devolución de llamada.

+0

¿no debería la última línea haber 'aguardado'? – niico

+15

No se escribió este código antes de que 'await' estuviera disponible. Esta es una devolución de llamada tradicional que utiliza controladores de eventos. 'await' se debe usar si se usa el nuevo' SendMailAsync'. – TheCodeKing

+1

SmtpException: error al enviar correo .--> System.InvalidOperationException: una operación asíncrona no se puede iniciar en este momento. Las operaciones asíncronas solo pueden iniciarse dentro de un controlador o módulo asíncrono o durante ciertos eventos en el ciclo de vida de la página. Si se produjo esta excepción al ejecutar una página, asegúrese de que la página esté marcada <% @ Page Async = "true"%>. Esta excepción también puede indicar un intento de invocar un método "async void", que generalmente no está soportado en el procesamiento de solicitudes de ASP.NET. En cambio, el método asíncrono debería devolver una Tarea, y la persona que llama debe esperarlo. – Mrchief

12

En general, los objetos descartables deben eliminarse lo antes posible; la implementación de IDisposable en un objeto tiene la intención de comunicar el hecho de que la clase en cuestión contiene recursos costosos que deberían ser liberados determinísticamente. Sin embargo, si la creación de esos recursos es costosa y necesita construir muchos de estos objetos, puede ser mejor (en términos de rendimiento) mantener una instancia en la memoria y reutilizarla. Solo hay una forma de saber si eso hace alguna diferencia: ¡perfilarlo!

Re: eliminación y Async: no se puede usar using obviamente. En su lugar, normalmente disponer el objeto en caso SendCompleted:

var smtpClient = new SmtpClient(); 
smtpClient.SendCompleted += (s, e) => smtpClient.Dispose(); 
smtpClient.SendAsync(...); 
128

La pregunta original fue para .NET 4, pero si ayuda a partir de .NET 4.5 SmtpClient implementa el método asincrónico SendMailAsync.

Como resultado, para enviar correo electrónico de forma asíncrona es como la siguiente:

public async Task SendEmail(string toEmailAddress, string emailSubject, string emailMessage) 
{ 
    using (var message = new MailMessage()) 
    { 
     message.To.Add(toEmailAddress); 

     message.Subject = emailSubject; 
     message.Body = emailMessage; 

     using (var smtpClient = new SmtpClient()) 
     { 
      await smtpClient.SendMailAsync(message); 
     } 
    } 
} 

Es mejor evitar el uso de método SendAsync.

+0

¿Por qué es mejor evitarlo? Creo que depende de los requisitos. – Jowen

+10

SendMailAsync() es un envoltorio alrededor del método SendAsync() de todos modos. async/await es mucho más limpio y elegante. Conseguiría exactamente los mismos requisitos. –

+0

@BorisLipschitz ¿Hay alguna manera de pasar valores al método de devolución de llamada en SendMailAsync como había en SendAsync? UserState ahora es del tipo TaskCompletionSource . –

5

Ok, la vieja pregunta que sé. Pero me encontré con esto cuando necesitaba implementar algo similar. Solo quería compartir un código.

Estoy iterando sobre varios SmtpClients para enviar varios correos de forma asincrónica. Mi solución es similar a TheCodeKing, pero, en su lugar, estoy deshaciendo el objeto de devolución de llamada. También estoy pasando MailMessage como userToken para obtenerlo en el evento SendCompleted para que yo también pueda llamar a disponer de eso.De esta manera:

foreach (Customer customer in Customers) 
{ 
    SmtpClient smtpClient = new SmtpClient(); //SmtpClient configuration out of this scope 
    MailMessage message = new MailMessage(); //MailMessage configuration out of this scope 

    smtpClient.SendCompleted += (s, e) => 
    { 
     SmtpClient callbackClient = s as SmtpClient; 
     MailMessage callbackMailMessage = e.UserState as MailMessage; 
     callbackClient.Dispose(); 
     callbackMailMessage.Dispose(); 
    }; 

    smtpClient.SendAsync(message, message); 
} 
+1

¿Es la mejor práctica crear un nuevo SmtpClient para cada correo electrónico para enviar? –

+1

Sí, para el envío asíncrono, siempre que disponga del cliente en la devolución de llamada ... – jmelhus

+1

¡gracias! y solo por el bien de una breve explicación: www.codefrenzy.net/2012/01/30/how-asynchronous-is-smtpclient-sendasync –

4

Se puede ver por qué es particularmente importante disponer de SmtpClient por el siguiente comentario:

public class SmtpClient : IDisposable 
    // Summary: 
    //  Sends a QUIT message to the SMTP server, gracefully ends the TCP connection, 
    //  and releases all resources used by the current instance of the System.Net.Mail.SmtpClient 
    //  class. 
    public void Dispose(); 

En mi envío de múltiples correos utilizando Gmail sin disponer el cliente escenario, solía consiga:

Mensaje: Servicio no disponible, cerrando el canal de transmisión. La respuesta del servidor fue: 4.7.0 Problema temporal del sistema. Vuelve a intentarlo más tarde (WS). oo3sm17830090pdb.64 - gsmtp

+0

Gracias por compartir su excepción aquí ya que estaba enviando clientes SMTP sin deshacerse hasta el momento. Aunque estoy usando mi propio servidor SMTP, siempre se debe considerar una buena práctica de programación. Mirando desde su error, ahora recibí advertencias y rectificaré mi código para incluir funciones de eliminación para garantizar la confiabilidad de la plataforma. – vibs2006

Cuestiones relacionadas