Два способа отправки электронной почты через SmtpClient асинхронно, разные результаты - PullRequest
8 голосов
/ 07 января 2012

Простая концепция здесь.Это для сайта, созданного с использованием 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);
    }
}

Это сработало половину времени. Другая половинаЯ получаю сообщение об ошибке «Не удается получить доступ к удаленному объекту» в 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);

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

Вопросы:

1) что не так в первом методе, который вызывает ошибки «удаленного объекта»?

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

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

Спасибо.

Ответы [ 3 ]

7 голосов
/ 07 января 2012

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

5 голосов
/ 21 апреля 2012

подсказки: 1-не используйте «использование блока» для объекта MailMessage, он удаляет ваш объект перед отправкой почты3 набора 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;
    }
1 голос
/ 21 апреля 2012

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

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

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

...