Подтвердить что ты не робот

Асинхронная отправка писем в С#?

Я разрабатываю приложение, в котором пользователь нажимает/нажимает кнопку ввода на определенной кнопке в окне, приложение выполняет некоторые проверки и определяет, отправлять ли пару писем или нет, а затем показывать другое окно с сообщением.

Моя проблема заключается в том, что отправка двух писем замедляет процесс заметно, а для некоторых (~ 8) секунд первое окно выглядит замороженным во время отправки.

Есть ли способ отправить эти письма на задний план и сразу отобразить следующее окно?

Пожалуйста, не ограничивайте свой ответ "используйте X-класс" или "просто используйте метод X", поскольку я еще не слишком хорошо знаком с языком, и вам будет очень полезно получить дополнительную информацию.

Спасибо.

4b9b3361

Ответ 1

Начиная с .NET 4.5 SmtpClient реализует асинхронный ожидаемый метод SendMailAsync. В результате для асинхронного посылки электронной почты следует:

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

Ответ 2

В качестве небольшой единицы работы вы должны использовать ThreadPool.QueueUserWorkItem для аспекта Threading. Если вы используете класс SmtpClient для отправки своей почты, вы можете обработать SendCompleted, чтобы дать отзыв пользователю.

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

Создавая новый SMTP-клиент каждый раз, это позволит вам отправлять электронные письма одновременно.

Ответ 3

Не слишком сложно просто отправить сообщение в отдельный поток:

using System.Net.Mail;

Smtp.SendAsync(message);

Или, если вы хотите построить целое сообщение в отдельном потоке, а не просто отправить его асинхронно:

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

Ответ 4

Метод SmtpClient.SendAsync

Пример

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

Ответ 5

Просто потому, что это немного расплывчато... Я буду кратким...

Существует много способов сделать асинхронную или параллельную работу в С#/.NET и т.д.

Самый быстрый способ сделать то, что вы хотите, - использовать поток рабочего фона, который позволит избежать блокировки вашего пользовательского интерфейса.

Совет с фоновыми рабочими потоками: вы не можете напрямую обновлять пользовательский интерфейс от них (привязка потоков и Marshalling - это то, чему вы научились справляться...)

Еще одна вещь, которая стоит рассмотреть... если вы используете стандартную информацию типа System.Net.Mail для отправки электронных писем... будьте осторожны, как вы создаете свою логику. Если вы изолируете все это в каком-то методе и вызывают его снова и снова, ему, вероятно, придется каждый раз сбрасывать и перестраивать соединение с почтовым сервером, а латентность, связанная с аутентификацией и т.д., Все равно замедлит все это без необходимости. Отправляйте несколько электронных писем через одно открытое соединение с почтовым сервером, когда это возможно.

Ответ 6

Попробуйте следующее:

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

Ответ 7

Ниже приведен пример fire and forget async с использованием .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);
});

где BackgroundTaskRunner:

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

Работает как прелесть в Azure App Services.

Ответ 8

Используйте класс SmtpClient и используйте метод SendAsync в пространстве имен System.Net.Mail.

Ответ 9

Что вы хотите сделать, так это запустить задачу электронной почты в отдельном потоке, чтобы основной код мог продолжить обработку, в то время как другой поток выполняет работу по электронной почте.

Вот учебник о том, как это сделать: Threading Tutorial С#

Ответ 10

Самое простое решение - создать BackgroundWorker и направить сообщения в очередь. Затем просто позвольте BackgroundWorker пройти очередь и отправить каждую почту.

См. также Как выполнить операцию в фоновом режиме