Два способа отправки электронной почты через 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);
    }
}

Это работало половину времени. В другой половине я бы получил ошибку "не могу получить доступ к удаленному объекту" в обратном вызове.

Следующая реализация (от члена с солидной репутацией: каковы рекомендации по использованию 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-секундная задержка бессмысленной?

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

Спасибо.

3 8

3 ответа:

Ваш метод кулака не будет работать должным образом из-за блока USING. После завершения использования блока SmtpClient объект будет удален. Таким образом, вы не можете получить доступ к нему в обработчике событий.

Советы: 1-Не используйте "using block" Для объекта MailMessage, он размещает ваш объект перед отправкой почты
2-утилизируйте объекты MailMessage на SmtpClient.Событие SendCompleted:

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

3-set SendCompletedEventHandler для smtpClient объекта

smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);

4-еще код:

private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
    {
        // Get the unique identifier for this asynchronous operation.
        String token = (string)e.UserState;

        if (e.Cancelled)
        {
            //write your code here
        }
        if (e.Error != null)
        {
            //write your code here
        }
        else //mail sent
        {
            //write your code here
        }

        mailSent = true;
    }

SmtpClient.SendAsync является предпочтительным методом асинхронной отправки электронной почты, так как он использует методы SmtpClient, специально разработанные для этой цели. Он также проще в реализации и, как было доказано, работает тысячи раз.

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

SmtpClient.SendAsync на самом деле будет отправлять только асинхронно, если ваш метод доставки не SpecifiedPickupDirectory или PickupDirectoryFromIis. В в этих случаях он запишет файл сообщения в папку pickup перед возвращением. Проверьте раздел вашего конфигурационного файла <smtp>. Я предполагаю, что вы используете один из этих методов,и проблема заключается в папке pickup. Удалите старые файлы, которые у вас могут быть, и проверьте, не является ли проблема вашей антивирусной программой, которая, скорее всего, ищет вирусы в каждом новом файле. Проверьте, установлены ли атрибуты шифрования или сжатия. Может быть и что-то еще. Лучший способ проверить, является ли папка источником проблема заключается в ручном копировании файла электронной почты в него.