在.NET 4.0下使用SmtpClient,SendAsync和Dispose的最佳实践是什么


116

我现在对如何管理SmtpClient感到困惑,因为它是一次性的,尤其是当我使用SendAsync进行呼叫时。大概在SendAsync完成之前,我不应该调用Dispose。但是我应该调用它吗(例如,使用“ using”)。该方案是WCF服务,该服务会在拨打电话时定期发送电子邮件。大多数计算速度很快,但是发送电子邮件可能需要一秒钟左右的时间,因此Async是更可取的。

每次发送邮件时都应该创建一个新的SmtpClient吗?我应该为整个WCF创建一个吗?救命!

更新如果有所不同,则始终为用户定制每封电子邮件。WCF托管在Azure上,Gmail用作邮件程序。


1
请参阅有关如何处理IDisposable接口和异步大局观这个帖子:stackoverflow.com/questions/974945/...
克里斯·哈斯

Answers:


139

注意:.NET 4.5 SmtpClient实现async awaitablemethod SendMailAsync。对于较低版本,请SendAsync按照以下说明使用。


您应该始终IDisposable尽早处置实例。对于异步调用,这是在消息发送后的回调中。

var message = new MailMessage("from", "to", "subject", "body"))
var client = new SmtpClient("host");
client.SendCompleted += (s, e) => {
                           client.Dispose();
                           message.Dispose();
                        };
client.SendAsync(message, null);

SendAsync不接受回调有点烦人。


最后一行不应该“等待”吗?
niico

19
没有await可用之前未编写此代码。这是使用事件处理程序的传统回调。await如果使用较新的,则应使用SendMailAsync
TheCodeKing 2015年

3
SmtpException:发送邮件失败。-> System.InvalidOperationException:暂时无法启动异步操作。异步操作只能在异步处理程序或模块内或在页面生命周期中的某些事件期间启动。如果在执行页面时发生此异常,请确保将该页面标记为<%@ Page Async =“ true”%>。此异常也可能表示尝试调用“异步无效”方法,ASP.NET请求处理通常不支持该方法。相反,异步方法应返回一个Task,而调用者应等待它。
Mrchief 2015年

1
提供null第二个参数是否安全SendAsync(...)
jocull

166

最初的问题是针对.NET 4提出的,但如果它对.NET 4.5有所帮助,则SmtpClient实现异步等待方法 SendMailAsync

结果,异步发送电子邮件如下:

public async Task SendEmail(string toEmailAddress, string emailSubject, string emailMessage)
{
    using (var message = new MailMessage())
    {
        message.To.Add(toEmailAddress);

        message.Subject = emailSubject;
        message.Body = emailMessage;

        using (var smtpClient = new SmtpClient())
        {
            await smtpClient.SendMailAsync(message);
        }
    }
}

最好避免使用SendAsync方法。


为什么最好避免呢?我认为这取决于要求。
2014年

14
无论如何,SendMailAsync()是SendAsync()方法的包装。异步/等待更整洁,更优雅。它将达到完全相同的要求。
鲍里斯·李普斯基兹

2
@RodHartzell,您可以随时使用.ContinueWith()
Boris Lipschitz

2
使用-或处置-或没有实际区别会更好吗?在最后一个“使用”块中,是否有可能在执行SendMailAsync之前将smtpClient处理掉?
niico

6
MailMessage也应处置。
TheCodeKing 2015年

16

通常,应尽快处理IDisposable对象。在对象上实现IDisposable旨在传达以下事实:所涉及的类拥有应确定释放的昂贵资源。但是,如果创建这些资源的成本很高,并且您需要构造许多这些对象,则将一个实例保留在内存中并重用它可能会更好(在性能方面)。只有一种方法知道是否有任何不同:剖析它!

回复:处置和异步:你using显然不能使用。相反,通常将对象放置在SendCompleted事件中:

var smtpClient = new SmtpClient();
smtpClient.SendCompleted += (s, e) => smtpClient.Dispose();
smtpClient.SendAsync(...);

6

好的,我知道一个老问题。但是当我需要实现类似的东西时,我自己偶然发现了这一点。我只是想分享一些代码。

我正在迭代多个SmtpClients以异步发送多个邮件。我的解决方案与TheCodeKing相似,但我将配置回调对象。我还将MailMessage作为userToken传递给它,以在SendCompleted事件中获取它,因此我也可以对其进行调用dispose。像这样:

foreach (Customer customer in Customers)
{
    SmtpClient smtpClient = new SmtpClient(); //SmtpClient configuration out of this scope
    MailMessage message = new MailMessage(); //MailMessage configuration out of this scope

    smtpClient.SendCompleted += (s, e) =>
    {
        SmtpClient callbackClient = s as SmtpClient;
        MailMessage callbackMailMessage = e.UserState as MailMessage;
        callbackClient.Dispose();
        callbackMailMessage.Dispose();
    };

    smtpClient.SendAsync(message, message);
}

2
最好的做法是为要发送的每封电子邮件创建一个新的SmtpClient?
马丁科尔

1
是的,对于异步发送,只要您在回调中处理客户端...
jmelhus 2014年

1
谢谢!和公正的简要解释的原因:www.codefrenzy.net/2012/01/30/how-asynchronous-is-smtpclient-sendasync
马丁科尔

1
这是我在smtpclient.sendAsync函数及其相关处置处理的stackoverflow上找到的最简单,准确的答案之一。我写了一个异步批量邮件发送库。当我每隔几分钟发送50条以上消息时,执行处置方法对我来说是非常重要的一步。这段代码正是帮助我实现了这一目标。我会在多线程环境中在此代码中发现一些错误的情况下答复。
vibs2006

1
我可以说,除非您有能力配置交换服务器(如果使用),否则在循环发送100多个电子邮件时,这不是一个好方法。服务器可能会抛出异常4.3.2 The maximum number of concurrent connections has exceeded a limit, closing trasmission channel。而是尝试仅使用一个实例SmtpClient
ibubi

6

您可以通过以下注释了解为什么处置SmtpClient尤为重要:

public class SmtpClient : IDisposable
   // Summary:
    //     Sends a QUIT message to the SMTP server, gracefully ends the TCP connection,
    //     and releases all resources used by the current instance of the System.Net.Mail.SmtpClient
    //     class.
    public void Dispose();

在我的情况下使用Gmail发送多封邮件而不处理客户端的情况下,我曾经得到:

消息:服务不可用,正在关闭传输通道。服务器响应为:4.7.0临时系统问题。请稍后再试(WS)。oo3sm17830090pdb.64-gsmtp


1
感谢您在这里分享您的例外情况,因为我到目前为止尚未发送SMTP客户端。尽管我使用自己的SMTP服务器,但应始终考虑良好的编程习惯。从您的错误来看,我现在已经有了一些警告,并将纠正我的代码以包括处理函数,以确保平台的可靠性。
vibs2006
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.