¿Cuáles son las mejores prácticas para usar SmtpClient, SendAsync y Dispose en .NET 4.0?


116

Estoy un poco perplejo sobre cómo administrar SmtpClient ahora que es desechable, especialmente si hago llamadas usando SendAsync. Es de suponer que no debería llamar a Dispose hasta que se complete SendAsync. Pero, ¿debería llamarlo alguna vez (por ejemplo, usando "using"). 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 del correo electrónico puede tardar aproximadamente un segundo, por lo que sería preferible Async.

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

Actualización En caso de que marque la diferencia, cada correo electrónico siempre está personalizado para el usuario. WCF está alojado en Azure y Gmail se usa como correo.


1
Vea esta publicación sobre el panorama general sobre cómo manejar IDisposable y async: stackoverflow.com/questions/974945/…
Chris Haas

Respuestas:


139

Nota: .NET 4.5 SmtpClient implementa el async awaitablemétodo SendMailAsync. Para versiones inferiores, utilice SendAsynccomo se describe a continuación.


Siempre debe deshacerse de las IDisposableinstancias lo antes posible. En el caso de las llamadas asíncronas, esto está en la devolución de llamada después de que se envía 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 SendAsyncque no acepte una devolución de llamada.


¿No debería la última línea tener 'aguardar'?
niico

19
No, este código fue escrito antes de que awaitestuviera disponible. Esta es una devolución de llamada tradicional que utiliza controladores de eventos. awaitdebe usarse si se usa el más nuevo SendMailAsync.
TheCodeKing

3
SmtpException: error al enviar correo .--> System.InvalidOperationException: no se puede iniciar una operación asincrónica en este momento. Las operaciones asincrónicas solo pueden iniciarse dentro de un controlador o módulo asincrónico o durante ciertos eventos en el ciclo de vida de la página. Si esta excepción ocurrió mientras se ejecutaba una página, asegúrese de que la página esté marcada como <% @ Page Async = "true"%>. Esta excepción también puede indicar un intento de llamar a un método "async void", que generalmente no es compatible con el procesamiento de solicitudes ASP.NET. En su lugar, el método asincrónico debería devolver una tarea y la persona que llama debería esperarla.
Mrchief

1
¿Es seguro proporcionarlo nullcomo segundo parámetro SendAsync(...)?
jocull

167

La pregunta original se hizo para .NET 4, pero si ayuda a partir de .NET 4.5 SmtpClient implementa un método asincrónico en espera SendMailAsync.

Como resultado, enviar correo electrónico de forma asincrónica es el 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 del método SendAsync.


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

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

2
@RodHartzell siempre puedes usar .ContinueWith ()
Boris Lipschitz

2
¿Es mejor usar usando, o desechar, o sin diferencia práctica? ¿No es posible en ese último bloque de 'uso' que smtpClient pueda eliminarse antes de que se ejecute SendMailAsync?
niico

6
MailMessagetambién debe desecharse.
TheCodeKing

16

En general, los objetos desechables deben desecharse lo antes posible; La implementación de IDisposable en un objeto pretende comunicar el hecho de que la clase en cuestión contiene recursos costosos que deberían liberarse de forma determinista. 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: desechar y Async: usingobviamente no se puede usar . En su lugar, normalmente desecha el objeto en el evento SendCompleted:

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

6

Ok, vieja pregunta, lo sé. Pero me encontré con esto yo mismo 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 estoy desechando el objeto de devolución de llamada. También estoy pasando MailMessage como userToken para obtenerlo en el evento SendCompleted para que pueda llamar a dispose también. Me gusta esto:

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

2
¿Es la mejor práctica crear un nuevo SmtpClient para cada correo electrónico que se envíe?
Martín Coll

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

1
¡Gracias! y solo por una breve explicación: www.codefrenzy.net/2012/01/30/how-asynchronous-is-smtpclient-sendasync
Martín Coll

1
Esta es una de las respuestas más simples y precisas que encontré en stackoverflow para la función smtpclient.sendAsync y su manejo de eliminación relacionado. Escribí una biblioteca de envío de correo masivo asincrónica. Como envío más de 50 mensajes cada pocos minutos, ejecutar el método de eliminación fue un paso muy importante para mí. Este código me ayudó exactamente a lograrlo. Responderé en caso de que encuentre algunos errores en este código durante los entornos de subprocesos múltiples.
vibs2006

1
Puedo decir que no es un buen enfoque cuando envía más de 100 correos electrónicos en un bucle, a menos que tenga la capacidad de configurar el servidor de intercambio (si lo usa). El servidor puede lanzar una excepción como 4.3.2 The maximum number of concurrent connections has exceeded a limit, closing trasmission channel. En su lugar, intente usar solo una instancia deSmtpClient
ibubi

6

Puede ver por qué es particularmente importante deshacerse de SmtpClient mediante 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 escenario, enviando varios correos electrónicos usando Gmail sin deshacerme del cliente, solía obtener:

Mensaje: Servicio no disponible, cerrando 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


1
Gracias por compartir su excepción aquí, ya que estaba enviando clientes SMTP sin eliminarlos hasta ahora. Aunque estoy usando mi propio servidor SMTP, siempre se debe considerar una buena práctica de programación. En vista de su error, ahora tengo advertencias y rectificaré mi código para incluir funciones de eliminación para garantizar la confiabilidad de la plataforma.
vibs2006
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.