web-dev-qa-db-fra.com

Envoi asynchrone d'emails en C #?

Je suis en train de développer une application où un utilisateur clique/appuie sur la touche Entrée d'un certain bouton dans une fenêtre, l'application effectue quelques vérifications et détermine s'il faut envoyer quelques e-mails ou non, puis afficher une autre fenêtre avec un message.

Mon problème est que l'envoi des 2 e-mails ralentit sensiblement le processus et pendant quelques (~ 8) secondes, la première fenêtre semble figée pendant l'envoi.

Existe-t-il un moyen d'envoyer ces e-mails en arrière-plan et d'afficher la fenêtre suivante immédiatement?

Veuillez ne pas limiter votre réponse avec "utiliser la classe X" ou "simplement utiliser la méthode X" car je ne suis pas encore trop familiarisé avec la langue et quelques informations supplémentaires seraient très appréciées.

Merci.

35
Eton B.

À partir de .NET 4.5 SmtpClient implémente la méthode asynchrone attendable SendMailAsync . Par conséquent, envoyer un e-mail de manière asynchrone est le suivant:

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

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

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

Comme il s'agit d'une petite unité de travail, vous devez utiliser ThreadPool.QueueUserWorkItem pour l'aspect threading de celui-ci. Si vous utilisez la classe SmtpClient pour envoyer votre courrier, vous pouvez gérer l'événement SendCompleted pour donner des commentaires à l'utilisateur.

ThreadPool.QueueUserWorkItem(t =>
{
    SmtpClient client = new SmtpClient("MyMailServer");
    MailAddress from = new MailAddress("[email protected]", "My Name", System.Text.Encoding.UTF8);
    MailAddress to = new MailAddress("[email protected]");
    MailMessage message = new MailMessage(from, to);
    message.Body = "The message I want to send.";
    message.BodyEncoding =  System.Text.Encoding.UTF8;
    message.Subject = "The subject of the email";
    message.SubjectEncoding = System.Text.Encoding.UTF8;
    // Set the method that is called back when the send operation ends.
    client.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
    // The userState can be any object that allows your callback 
    // method to identify this send operation.
    // For this example, I am passing the message itself
    client.SendAsync(message, message);
});

private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
{
        // Get the message we sent
        MailMessage msg = (MailMessage)e.UserState;

        if (e.Cancelled)
        {
            // Prompt user with "send cancelled" message 
        }
        if (e.Error != null)
        {
            // Prompt user with error message 
        }
        else
        {
            // Prompt user with message sent!
            // as we have the message object we can also display who the message
            // was sent to etc 
        }

        // finally dispose of the message
        if (msg != null)
            msg.Dispose();
}

En créant un nouveau client SMTP à chaque fois, cela vous permettra d'envoyer des e-mails simultanément.

24
James

Ce n'est pas trop compliqué d'envoyer simplement le message sur un thread séparé:

using System.Net.Mail;

Smtp.SendAsync(message);

Ou, si vous souhaitez construire l'intégralité du message sur le thread séparé au lieu de simplement l'envoyer de manière asynchrone:

using System.Threading;
using System.Net.Mail;

var sendMailThread = new Thread(() => {
    var message=new MailMessage();
    message.From="from e-mail";
    message.To="to e-mail";
    message.Subject="Message Subject";
    message.Body="Message Body";

    SmtpMail.SmtpServer="SMTP Server Address";
    SmtpMail.Send(message);
});

sendMailThread.Start();
12
Justin Niessner

méthode SmtpClient.SendAsync

Échantillon

using System;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Threading;
using System.ComponentModel;
namespace Examples.SmptExamples.Async
{
    public class SimpleAsynchronousExample
    {
        static bool mailSent = false;
        private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
        {
            // Get the unique identifier for this asynchronous operation.
             String token = (string) e.UserState;

            if (e.Cancelled)
            {
                 Console.WriteLine("[{0}] Send canceled.", token);
            }
            if (e.Error != null)
            {
                 Console.WriteLine("[{0}] {1}", token, e.Error.ToString());
            } else
            {
                Console.WriteLine("Message sent.");
            }
            mailSent = true;
        }
        public static void Main(string[] args)
        {
            // Command line argument must the the SMTP Host.
            SmtpClient client = new SmtpClient(args[0]);
            // Specify the e-mail sender. 
            // Create a mailing address that includes a UTF8 character 
            // in the display name.
            MailAddress from = new MailAddress("[email protected]", 
               "Jane " + (char)0xD8+ " Clayton", 
            System.Text.Encoding.UTF8);
            // Set destinations for the e-mail message.
            MailAddress to = new MailAddress("[email protected]");
            // Specify the message content.
            MailMessage message = new MailMessage(from, to);
            message.Body = "This is a test e-mail message sent by an application. ";
            // Include some non-ASCII characters in body and subject. 
            string someArrows = new string(new char[] {'\u2190', '\u2191', '\u2192', '\u2193'});
            message.Body += Environment.NewLine + someArrows;
            message.BodyEncoding =  System.Text.Encoding.UTF8;
            message.Subject = "test message 1" + someArrows;
            message.SubjectEncoding = System.Text.Encoding.UTF8;
            // Set the method that is called back when the send operation ends.
            client.SendCompleted += new 
            SendCompletedEventHandler(SendCompletedCallback);
            // The userState can be any object that allows your callback  
            // method to identify this send operation. 
            // For this example, the userToken is a string constant. 
            string userState = "test message1";
            client.SendAsync(message, userState);
            Console.WriteLine("Sending message... press c to cancel mail. Press any other key to exit.");
            string answer = Console.ReadLine();
            // If the user canceled the send, and mail hasn't been sent yet, 
            // then cancel the pending operation. 
            if (answer.StartsWith("c") && mailSent == false)
            {
                client.SendAsyncCancel();
            }
            // Clean up.
            message.Dispose();
            Console.WriteLine("Goodbye.");
        }
    }
}
8
Andrey

Voici une approche tirer et oublier avec async en utilisant .Net 4.5.2+:

BackgroundTaskRunner.FireAndForgetTaskAsync(async () =>
{
    SmtpClient smtpClient = new SmtpClient(); // using configuration file settings
    MailMessage message = new MailMessage(); // TODO: Initialize appropriately
    await smtpClient.SendMailAsync(message);
});

où BackgroundTaskRunner est:

public static class BackgroundTaskRunner
{     
    public static void FireAndForgetTask(Action action)
    {
        HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => // .Net 4.5.2+ required
        {
            try
            {
                action();
            }
            catch (Exception e)
            {
                // TODO: handle exception
            }
        });
    }

    /// <summary>
    /// Using async
    /// </summary>
    public static void FireAndForgetTaskAsync(Func<Task> action)
    {
        HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => // .Net 4.5.2+ required
        {
            try
            {
                await action();
            }
            catch (Exception e)
            {
                // TODO: handle exception
            }
        });
    }
}

Fonctionne comme un charme sur Azure App Services.

6
Augusto Barreto

Juste parce que c'est un peu vague ... je serai bref ...

Il existe de nombreuses façons de faire un travail asynchrone ou parallèle dans c # /. Net, etc.

Le moyen le plus rapide de faire ce que vous voulez est d'utiliser un thread de travail en arrière-plan qui évitera de verrouiller votre interface utilisateur.

Un conseil avec les threads de travail en arrière-plan: vous ne pouvez pas directement mettre à jour l'interface utilisateur à partir d'eux (l'affinité des threads et le marshaling sont juste quelque chose que vous apprenez à gérer ...)

Une autre chose à considérer ... si vous utilisez les trucs standard de type System.Net.Mail pour envoyer les e-mails ... soyez prudent dans la façon dont vous créez votre logique. Si vous isolez tout cela dans une méthode et l'appelez encore et encore, il devra probablement interrompre et reconstruire la connexion au serveur de messagerie à chaque fois et la latence impliquée dans l'authentification, etc., ralentira toujours le tout inutilement. Envoyez plusieurs e-mails via une seule connexion ouverte au serveur de messagerie lorsque cela est possible.

5
fdfrye

Essaye ça:

var client = new System.Net.Mail.SmtpClient("smtp.server");
var message = new System.Net.Mail.MailMessage() { /* provide its properties */ };
client.SendAsync(message, null);
4
kbrimington

Utilisez la classe SmtpClient et utilisez la méthode SendAsync dans l'espace de noms System.Net.Mail.

2
CubanX

Ce que vous voulez faire est d'exécuter la tâche de courrier électronique sur un thread séparé afin que le code principal puisse continuer le traitement pendant que l'autre thread effectue le travail de courrier électronique.

Voici un tutoriel sur la façon de le faire: Threading Tutorial C #

1
JohnFx

En utilisant Task Parallel Library dans .NET 4.0, vous pouvez faire:

Parllel.Invoke(() => { YourSendMailMethod(); });

Voir aussi cristina manu's blog sur Parallel.Invoke () vs gestion explicite des tâches.

1
JaredReisinger