Два способа асинхронной отправки электронной почты через SmtpClient, разные результаты

Простая концепция. Это для сайта, создаваемого с использованием MVC 3 и Entity Framework 4. После регистрации пользователя на сайте, на его электронный адрес отправляется письмо. Сначала я реализовал это с помощью SmtpClient.Send(), и все работало нормально. Затем мне пришла в голову светлая идея попробовать отправить письмо асинхронно. У меня возникли проблемы с двумя асинхронными подходами, которые я пробовал.

Первая реализация (из этого сообщения без ответов: https://stackoverflow.com/questions/7558582/how-to-dispose-using-smtpclient-send-and-asynccallback):

public bool Emailer(){
    .
    .
    .
    using (var smtpClient = new SmtpClient())
    {
        smtpClient.EnableSsl = true;
        smtpClient.Host = "smtp.gmail.com";
        smtpClient.Port = 587;
        smtpClient.UseDefaultCredentials = false;
        smtpClient.Credentials = new NetworkCredential("myaddress@gmail.com", "mypassword");

        var sd = new SendEmailDelegate(smtpClient.Send);
        var cb = new AsyncCallback(SendEmailResponse);
        sd.BeginInvoke(message, cb, sd);

        return true;
    }
}

private delegate void SendEmailDelegate(System.Net.Mail.MailMessage m);

private static void SendEmailResponse(IAsyncResult ar)
{
    try
    {
        SendEmailDelegate sd = (SendEmailDelegate)(ar.AsyncState);
        sd.EndInvoke(ar); // "cannot access a disposed object" errors here
    }
    catch (Exception e)
    {
        _logger.WarnException("Error on EndInvoke.", e);
    }
}

Это работало половину времени. В другой половине я получал ошибку "Cannot access a disposed object" в CallBack.

Следующая реализация (от участника с солидной репутацией: Каковы лучшие практики использования SmtpClient, SendAsync и Dispose в .NET 4.0):

var smtpClient = new SmtpClient();
smtpClient.EnableSsl = true;
smtpClient.Host = "smtp.gmail.com";
smtpClient.Port = 587;
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials = new NetworkCredential("myaddress@gmail.com", "mypassword");

smtpClient.SendCompleted += (s, e) =>
    {
        smtpClient.Dispose();
        message.Dispose();
    };
smtpClient.SendAsync(message, null);

При такой реализации я не получаю никаких ошибок, но в режиме отладки заметно увеличивается задержка (~5 секунд) при выполнении smtpClient.SendAsync(), что наводит меня на мысль, что сообщение отправляется не асинхронно.

Вопросы:

1) что не так в первом методе, что вызывает ошибки "disposed object"?

2) есть ли проблема во второй реализации, из-за которой письмо не отправляется асинхронно? Является ли 5-секундная задержка бессмысленной?

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

Спасибо.

8
задан Community 23 May 2017 в 12:02
поделиться