繁体   English   中英

使用SmtpClient时发送电子邮件

[英]Send email when using SmtpClient

我创建了发送失败的电子邮件的循环

我读过,在代码中使用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));
}

我想补充丹尼斯的答案。

这里是SmtpClient包装器的简化实现,它允许您将重试计数参数与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);

上面的代码片段中有很多简化,但是您应该了解主要思想。

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

以我的经验,最好让电子邮件服务器(或服务)处理电子邮件。 电子邮件服务器旨在重试传递,直到传递成功或永久失败。 如果您选择本地(例如IIS的内置电子邮件服务)或附近(例如您的ISP)的电子邮件服务器,则应该保证它始终可用。 配置SmtpClient以在此处发送电子邮件。 由于服务器是关闭的,因此这应该不会失败,也不会花费很长时间,从而消除了在应用程序中重试的需要。

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

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

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

这并不像您想的那么容易。 编写起来很容易,但是要使其真正起作用是很棘手的:)。 我建议您看看此SMTP发送器 我花了一些时间尝试编写自己的电子邮件发件人,但最终找到了这个工作库。 我遇到的问题之一就是使用GMail,此问题已在此处解决。 我没有与作者保持任何联系,但我强烈建议您这样做。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM