web-dev-qa-db-fra.com

Quelles sont les meilleures pratiques pour utiliser SmtpClient, SendAsync et Dispose sous .NET 4.0

Je suis un peu perplexe sur la façon de gérer SmtpClient maintenant qu'il est disponible, surtout si je passe des appels avec SendAsync. Je suppose que je ne devrais pas appeler Dispose avant la fin de SendAsync. Mais devrais-je jamais l'appeler (par exemple, en utilisant "en utilisant"). Le scénario est un service WCF qui envoie périodiquement un courrier électronique lorsque des appels sont passés. La plupart des calculs sont rapides, mais l'envoi d'un courrier électronique peut prendre une seconde ou deux, alors Async serait préférable.

Dois-je créer un nouveau SmtpClient chaque fois que j'envoie un courrier? Dois-je en créer un pour l'ensemble de la WCF? Aidez-moi!

Mise à jour Au cas où cela ferait une différence, chaque email est toujours personnalisé pour l'utilisateur. Le WCF est hébergé sur Azure et Gmail est utilisé comme mailer.

108
tofutim

Remarque: .NET 4.5 SmtpClient implémente async awaitable méthode SendMailAsync. Pour les versions inférieures, utilisez SendAsync comme décrit ci-dessous.


Vous devez toujours disposer de IDisposable instances dans les meilleurs délais. Dans le cas d'appels asynchrones, c'est sur le rappel après l'envoi du message.

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

C'est un peu gênant que SendAsync n'accepte pas de rappel.

127
TheCodeKing

La question initiale avait été posée pour .NET 4, mais si cela l’aiderait à partir de .NET 4.5, SmtpClient implémente la méthode async attendue SendMailAsync .

En conséquence, envoyer un courrier électronique de manière asynchrone est le suivant:

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

Il est préférable d'éviter d'utiliser la méthode SendAsync.

159
Boris Lipschitz

En général, les objets identifiables doivent être éliminés dès que possible. implémenter IDisposable sur un objet est destiné à communiquer le fait que la classe en question contient des ressources coûteuses qui devraient être libérées de manière déterministe. Toutefois, si la création de ces ressources est coûteuse et que vous devez construire un grand nombre de ces objets, il peut être préférable (en termes de performances) de conserver une instance en mémoire et de la réutiliser. Il n'y a qu'un moyen de savoir si cela fait une différence: profilez-le!

Re: Disposing et Async: vous ne pouvez évidemment pas utiliser using. Au lieu de cela, vous disposez généralement de l'objet dans l'événement SendCompleted:

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

Vous pouvez voir pourquoi il est particulièrement important de disposer de SmtpClient par le commentaire suivant:

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

Dans mon scénario d’envoi de plusieurs courriels à l’aide de Gmail sans disposer du client, j’obtenais:

Message: Service non disponible, fermeture du canal de transmission. La réponse du serveur était: 4.7.0 Problème système temporaire. Réessayez plus tard (WS). oo3sm17830090pdb.64 - gsmtp

6
Anton Skovorodko

Ok, vieille question je sais. Mais je suis tombé sur cela moi-même quand j'avais besoin de mettre en œuvre quelque chose de similaire. Je voulais juste partager du code.

Je répète sur plusieurs SmtpClients pour envoyer plusieurs mails de manière asynchrone. Ma solution est similaire à TheCodeKing, mais je dispose à la place de l'objet de rappel. Je passe également MailMessage en tant que userToken pour l'obtenir dans l'événement SendCompleted afin que je puisse également appeler dispose à ce sujet. Comme ça:

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