通过SmtpClient异步发送邮件的两种方式,结果不同
这里有简单的概念。这是针对使用 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("[email protected]", "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);
}
}
这有效了一半的时间。另一半我会在回调中收到“无法访问已处置的对象”错误。
下一个实现(来自具有良好声誉的成员:在 .NET 4.0 下使用 SmtpClient、SendAsync 和 Dispose 的最佳实践是什么):
var smtpClient = new SmtpClient();
smtpClient.EnableSsl = true;
smtpClient.Host = "smtp.gmail.com";
smtpClient.Port = 587;
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials = new NetworkCredential("[email protected]", "mypassword");
smtpClient.SendCompleted += (s, e) =>
{
smtpClient.Dispose();
message.Dispose();
};
smtpClient.SendAsync(message, null);
使用此实现我没有收到任何错误,但是当 smtpClient.SendAsync() 执行时,调试模式下有明显较长的延迟(约 5 秒),导致我认为它不是异步发送的。
问题:
1)第一个方法有什么问题导致“已处置对象”错误?
2)第二个实现是否存在导致电子邮件无法异步发送的问题? 5秒的延迟没有意义吗?
可能还需要注意的是,虽然该网站不需要支持发送大量电子邮件(仅用户注册、选择加入时事通讯等),但我们确实预计未来会有大量用户,因此我决定异步发送电子邮件。
谢谢。
Simple concept here. This is for a site being built using MVC 3 and Entity Framework 4. After a user registers on the site, an email is sent to their email address. I first implemented this using SmtpClient.Send() and it worked fine. Then I got the bright idea to try sending the email asynchronously. I'm experiencing issues with the two async approaches I've tried.
First implementation (from this unanswered post: 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("[email protected]", "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);
}
}
This worked half the time. The other half I would get a "Cannot access a disposed object" error in the CallBack.
Next implementation (from a member with solid reputation: What are best practices for using SmtpClient, SendAsync and Dispose under .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("[email protected]", "mypassword");
smtpClient.SendCompleted += (s, e) =>
{
smtpClient.Dispose();
message.Dispose();
};
smtpClient.SendAsync(message, null);
With this implementation I dont get any errors, but there's a noticeably longer delay (~5 seconds) in debugging mode when smtpClient.SendAsync() executes, leading me to think that its not being sent asynchronously.
Questions:
1) what's wrong in the first method that's causing the "disposed object" errors?
2) does the second implementation have a problem that's causing the email to not send asynchronously? Is the 5-second delay meaningless?
Might be also important to note that although the site will not need to support sending a large number of emails (only user registration, opt-in newsletters, etc), we do anticipate a large number of users in the future, hence my decision to send emails asynchronously.
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
由于 USING 块,您的第一种方法将无法正常工作。 using-block 结束后,SmtpClient 对象将被释放。因此您无法在事件处理程序中访问它。
Your fist method will not work properly because of the USING-block. After the using-block ends, the SmtpClient object will de disposed. So you can't get access to it in your event handler.
尖端:
1-不要对 MailMessage 对象使用“using block”,它会在发送邮件之前处理您的对象
2-在 SmtpClient.SendCompleted 事件上处理 MailMessage 对象:
3-为 smtpClient 对象设置 SendCompletedEventHandler
4-更多代码:
tips:
1-dont use "using block" for MailMessage objet, it dispose your object before mail sent
2-dispose MailMessage objects on SmtpClient.SendCompleted event:
3-set SendCompletedEventHandler for smtpClient object
4-more code:
SmtpClient.SendAsync
是异步电子邮件发送的首选方法,因为它使用专门为此目的设计的SmtpClient
方法。它也更容易实施,并且已被证明可以工作数千次。您的 5 秒延迟很奇怪,表明存在需要解决的问题。第一段代码只是掩盖了问题,但并没有消除它。
如果您的传送方法不是
SpecifiedPickupDirectory
或PickupDirectoryFromIis
,SmtpClient.SendAsync
实际上只会异步发送。在这些情况下,它会在返回之前将消息文件写入拾取文件夹。检查配置文件的
部分。我的猜测是您正在使用其中一种方法,问题出在拾取文件夹上。删除那里可能有的旧文件,并检查问题是否出在您的防病毒软件上,该软件很可能会在每个新文件中搜索病毒。检查是否设置了加密或压缩属性。也可以是其他东西。测试该文件夹是否是问题根源的最佳方法是手动将电子邮件文件复制到其中。SmtpClient.SendAsync
is the preferred method of async email sending since it usesSmtpClient
methods specially designed for this purpose. It is also simpler to implement and has been proven to work thousands of times.Your 5 sec delay is strange and suggests there is a problem that needs addressing. First piece of code is just covering the problem up but does not eliminate it.
SmtpClient.SendAsync
will actually only send asynchronously if your delivery method is notSpecifiedPickupDirectory
orPickupDirectoryFromIis
. In those cases it will write the message file into the pickup folder before returning. Check your config file's<smtp>
section. My guess is you're using one of these methods and the problem is with the pickup folder. Delete old files you might have there and check if the problem is not your antivirus software which most likely searches each new file for viruses. Check if there is encryption or compression attributes set. Can be something else too. Best way to test if the folder is the source of the problems is by manually coping an email file into it.