通过SmtpClient异步发送邮件的两种方式,结果不同

发布于 2024-12-25 13:17:39 字数 2585 浏览 2 评论 0原文

这里有简单的概念。这是针对使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

思慕 2025-01-01 13:17:39

由于 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.

水水月牙 2025-01-01 13:17:39

尖端:
1-不要对 MailMessage 对象使用“using block”,它会在发送邮件之前处理您的对象
2-在 SmtpClient.SendCompleted 事件上处理 MailMessage 对象:

smtpClient.SendCompleted += (s, e) =>
    {
        message.Dispose();
    };

3-为 smtpClient 对象设置 SendCompletedEventHandler

smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);

4-更多代码:

private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
    {
        // Get the unique identifier for this asynchronous operation.
        String token = (string)e.UserState;

        if (e.Cancelled)
        {
            //write your code here
        }
        if (e.Error != null)
        {
            //write your code here
        }
        else //mail sent
        {
            //write your code here
        }

        mailSent = true;
    }

tips:
1-dont use "using block" for MailMessage objet, it dispose your object before mail sent
2-dispose MailMessage objects on SmtpClient.SendCompleted event:

smtpClient.SendCompleted += (s, e) =>
    {
        message.Dispose();
    };

3-set SendCompletedEventHandler for smtpClient object

smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);

4-more code:

private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)
    {
        // Get the unique identifier for this asynchronous operation.
        String token = (string)e.UserState;

        if (e.Cancelled)
        {
            //write your code here
        }
        if (e.Error != null)
        {
            //write your code here
        }
        else //mail sent
        {
            //write your code here
        }

        mailSent = true;
    }
莫相离 2025-01-01 13:17:39

SmtpClient.SendAsync 是异步电子邮件发送的首选方法,因为它使用专门为此目的设计的 SmtpClient 方法。它也更容易实施,并且已被证明可以工作数千次。

您的 5 秒延迟很奇怪,表明存在需要解决的问题。第一段代码只是掩盖了问题,但并没有消除它。

如果您的传送方法不是SpecifiedPickupDirectoryPickupDirectoryFromIisSmtpClient.SendAsync 实际上只会异步发送。在这些情况下,它会在返回之前将消息文件写入拾取文件夹。检查配置文件的 部分。我的猜测是您正在使用其中一种方法,问题出在拾取文件夹上。删除那里可能有的旧文件,并检查问题是否出在您的防病毒软件上,该软件很可能会在每个新文件中搜索病毒。检查是否设置了加密或压缩属性。也可以是其他东西。测试该文件夹是否是问题根源的最佳方法是手动将电子邮件文件复制到其中。

SmtpClient.SendAsync is the preferred method of async email sending since it uses SmtpClient 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 not SpecifiedPickupDirectory or PickupDirectoryFromIis. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文