使用SmtpClient时发送电子邮件

时间:2012-02-14 09:02:18

标签: c# 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));
}

4 个答案:

答案 0 :(得分:3)

我想补充丹尼斯的答案。

这里是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);

上面的代码片段有很多简化,但你应该理解主要的想法。

答案 1 :(得分:1)

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

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

在任何情况下,我都建议您实施日志记录以跟踪无法发送的任何电子邮件。

答案 2 :(得分:0)

您可以使用SmtpClient.SendAsync()

异步发送邮件
  

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

答案 3 :(得分:0)

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

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