Каковы лучшие практики для использования SmtpClient, SendAsync и Dispose под .NET 4.0 - PullRequest
108 голосов
/ 02 сентября 2011

Я немного озадачен тем, как управлять SmtpClient сейчас, когда он одноразовый, особенно если я звоню с использованием SendAsync.Предположительно я не должен вызывать Dispose, пока SendAsync не завершит работу.Но я должен когда-либо называть это (например, используя "использование").Сценарий - это служба WCF, которая периодически отправляет электронную почту при совершении звонков.Большая часть вычислений выполняется быстро, но отправка электронной почты может занять около секунды, поэтому предпочтительнее использовать Async.

Должен ли я создавать новый SmtpClient при каждой отправке почты?Должен ли я создать один для всего WCF?Справка!

Обновление Если это имеет значение, каждое электронное письмо всегда настраивается для пользователя.WCF размещается в Azure, а в качестве почтовой программы используется Gmail.

Ответы [ 5 ]

157 голосов
/ 18 марта 2014

Первоначальный вопрос был задан для .NET 4, но, если это помогает, начиная с .NET 4.5 SmtpClient реализует асинхронный ожидаемый метод SendMailAsync.

В результате асинхронная отправка электронной почты выполняется следующим образом:

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

Лучше избегать использования метода SendAsync.

127 голосов
/ 02 сентября 2011

Примечание. В .NET 4.5 SmtpClient реализован метод async awaitable SendMailAsync. Для более низких версий используйте SendAsync, как описано ниже.


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

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

Немного раздражает, что SendAsync не принимает обратный вызов.

16 голосов
/ 02 сентября 2011

Как правило, объекты IDisposable должны быть утилизированы как можно скорее;реализация IDisposable на объекте предназначена для передачи информации о том, что рассматриваемый класс содержит дорогие ресурсы, которые должны быть детерминированно освобождены.Однако, если создание этих ресурсов является дорогостоящим и вам необходимо создать множество этих объектов, может быть лучше (с точки зрения производительности) сохранить один экземпляр в памяти и использовать его повторно.Есть только один способ узнать, имеет ли это какое-то значение: профилировать его!

Re: утилизация и Async: вы не можете использовать using, очевидно.Вместо этого вы обычно располагаете объект в событии SendCompleted:

var smtpClient = new SmtpClient();
smtpClient.SendCompleted += (s, e) => smtpClient.Dispose();
smtpClient.SendAsync(...);
6 голосов
/ 20 августа 2014

Вы можете понять, почему так важно избавиться от SmtpClient, с помощью следующего комментария:

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

В моем сценарии, отправляющем несколько писем с помощью Gmail без использования клиента, я получал:

Сообщение: услуга недоступна, закрытие канала передачи. ответ сервера: 4.7.0 Временная системная проблема. Попробуйте позже (WS). oo3sm17830090pdb.64 - gsmtp

6 голосов
/ 18 июля 2014

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

Я перебираю несколько SmtpClients для асинхронной отправки нескольких писем.Мое решение похоже на TheCodeKing, но вместо этого я использую объект обратного вызова.Я также передаю MailMessage как userToken, чтобы получить его в событии SendCompleted, чтобы я мог также вызвать dispose для этого события.Как это:

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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...