【问题标题】:Send email when using SmtpClient使用 SmtpClient 时发送电子邮件
【发布时间】:2012-02-15 06:19:55
【问题描述】:

如果失败,我已经创建了发送电子邮件的循环

我已经读过,在代码中使用 thread.sleep 并不是最佳做法,因为这可能会导致问题。但是有用于 smtp 客户端,它在发送消息之前处理事务。我怎样才能使它更好地用于 Web 应用程序?

using (var SmtpClient = new SmtpClient()) {
    bool success = false;
    int attemts = 0;
    const int maxAttempts = 5;
    do {
        try {
            SmtpClient.Send(mailMessage);
            System.Threading.Thread.Sleep(400);
            success = true;
        } catch{
            // ok wait for request
            success = false;
            attemts ++;
        }
    } while (success && (attemts == maxAttempts));
}

【问题讨论】:

  • 刚刚在控制器中添加了异步结果,几乎完美

标签: c# smtpclient


【解决方案1】:

我只想补充丹尼斯的回答。

这里是 SmtpClient 包装器的简化实现,它允许您将 retry count 参数与 SendAsync 方法一起使用:

public class EmailSender {

    private int _currentRetryCount;

    private int _maxRetryCount;

    private MailMessage _mailMessage;

    private bool _isAlreadyRun;

    public event SendEmailCompletedEventHandler SendEmailCompleted;

    public void SendEmailAsync(MailMessage message, int retryCount) {

        if (_isAlreadyRun) {
            throw new InvalidOperationException(
              "EmailSender doesn't support multiple concurrent invocations."
            );
        }

        _isAlreadyRun = true;
        _maxRetryCount = retryCount;
        _mailMessage = message;

        SmtpClient client = new SmtpClient();
        client.SendCompleted += SmtpClientSendCompleted;

        SendMessage(client);
    }

    private void SendMessage(SmtpClient client) {
        try {
            client.SendAsync(_mailMessage, Guid.NewGuid());
        } catch (Exception exception) {
            EndProcessing(client);
        }
    }

    private void EndProcessing (SmtpClient client) {

        if (_mailMessage != null) {
            _mailMessage.Dispose();
        }

        if (client != null) {
            client.SendCompleted -= SmtpClientSendCompleted;
            client.Dispose();
        }

        OnSendCompleted(
           new SendEmailCompletedEventArgs(null, false, null, _currentRetryCount)
        );

        _isAlreadyRun = false;
        _currentRetryCount = 0;
    }

    private void SmtpClientSendCompleted(object sender, AsyncCompletedEventArgs e) {
        var smtpClient = (SmtpClient)sender;

        if(e.Error == null || _currentRetryCount >= _maxRetryCount) {
            EndProcessing(smtpClient);
        } else {
            _currentRetryCount++;
            SendMessage(smtpClient);
        }
    }

    protected virtual void OnSendCompleted(SendEmailCompletedEventArgs args) {
        var handler = SendEmailCompleted;
        if (handler != null) {
            handler(this, args);
        }
    }

}


public delegate void SendEmailCompletedEventHandler(
   object sender, SendEmailCompletedEventArgs e);

public class SendEmailCompletedEventArgs : AsyncCompletedEventArgs {
    public SendEmailCompletedEventArgs(
        Exception error,
        bool canceled,
        object userState,
        int retryCount)
        : base(error, canceled, userState) {
        RetryCount = retryCount;
    }

    public int RetryCount { get; set; }
}}

下面还有消费者代码示例:

        var sender = new EmailSender();

        sender.SendEmailCompleted += (o, eventArgs) 
            => Console.WriteLine(eventArgs.RetryCount);

        sender.SendEmailAsync(new MailMessage(), 5);

上面的代码片段有很多简化,但你应该明白大意。

【讨论】:

    【解决方案2】:

    我不确定您为什么需要此功能,但我假设您想确保发送电子邮件。

    根据我的经验,最好让电子邮件服务器(或服务)处理电子邮件。电子邮件服务器旨在重试传递,直到成功或永久失败。如果您选择本地的电子邮件服务器(如 IIS 的内置电子邮件服务)或附近的电子邮件服务器(例如您的 ISP),它应该几乎可以保证它始终可用。 配置SmtpClient 以在那里发送电子邮件。由于服务器就在附近,因此应该不会出现故障并且根本不需要很长时间,因此无需在您的应用程序中重试。

    无论如何,我建议您实施日志记录以跟踪任何无法发送的电子邮件。

    【讨论】:

      【解决方案3】:

      您可以使用SmtpClient.SendAsync()异步发送邮件

      将指定的电子邮件信息发送到 SMTP 服务器以进行传递。此方法不会阻塞调用线程,并允许调用者将对象传递给操作完成时调用的方法。 -- MSDN

      【讨论】:

        【解决方案4】:

        这并不像你想象的那么容易。编写它很容易,但让它真正工作却很棘手:)。 我建议你看看这个SMTP Sender。我花了一些时间尝试编写自己的电子邮件发件人,但最终找到了这个工作库。我遇到的问题之一是使用 GMail,这里解决了。我与作者没有任何联系,但我强烈推荐这个。

        顺便说一句 - 是的,.Net 似乎为您提供了一切,没有理由使用外部 lib,但是在您尝试几次之后,请记住这篇文章并尝试一下。

        【讨论】:

          猜你喜欢
          • 2015-06-11
          • 2014-09-15
          • 1970-01-01
          • 2015-02-26
          • 1970-01-01
          • 2011-07-03
          • 1970-01-01
          • 2013-06-04
          • 2022-06-13
          相关资源
          最近更新 更多