简体   繁体   English

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

[英]Two ways to send email via SmtpClient asynchronously, different results

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. 这适用于使用MVC 3和Entity Framework 4构建的站点。用户在站点上注册后,会向其电子邮件地址发送一封电子邮件。 I first implemented this using SmtpClient.Send() and it worked fine. 我首先使用SmtpClient.Send()实现它,它工作正常。 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 ): 第一个实现(来自这个未回答的帖子: 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("myaddress@gmail.com", "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. 另一半我会在CallBack中收到“无法访问已处置对象”错误。

Next implementation (from a member with solid reputation: What are best practices for using SmtpClient, SendAsync and Dispose under .NET 4.0 ): 下一个实现(来自具有良好声誉的成员: 在.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("myaddress@gmail.com", "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. 有了这个实现,我没有得到任何错误,但是当执行smtpClient.SendAsync()时,在调试模式下有明显更长的延迟(~5秒),这使我认为它不是异步发送的。

Questions: 问题:

1) what's wrong in the first method that's causing the "disposed object" errors? 1)导致“处置对象”错误的第一种方法出了什么问题?

2) does the second implementation have a problem that's causing the email to not send asynchronously? 2)第二个实现是否存在导致电子邮件不异步发送的问题? Is the 5-second delay meaningless? 5秒延迟是无意义的吗?

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. 谢谢。

Your fist method will not work properly because of the USING-block. 由于USING-block,你的第一种方法无法正常工作。 After the using-block ends, the SmtpClient object will de disposed. 使用块结束后,将释放SmtpClient对象。 So you can't get access to it in your event handler. 因此,您无法在事件处理程序中访问它。

tips: 1-dont use "using block" for MailMessage objet, it dispose your object before mail sent 提示:1 - 不要对MailMessage对象使用“使用块”,它会在邮件发送之前处理您的对象
2-dispose MailMessage objects on SmtpClient.SendCompleted event: 2在SmtpClient.SendCompleted事件上配置MailMessage对象:

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

3-set SendCompletedEventHandler for smtpClient object 用于smtpClient对象的3组SendCompletedEventHandler

smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);

4-more code: 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;
    }

SmtpClient.SendAsync is the preferred method of async email sending since it uses SmtpClient methods specially designed for this purpose. SmtpClient.SendAsync是异步电子邮件发送的首选方法,因为它使用专门为此目的设计的SmtpClient方法。 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. 你的5秒延迟是奇怪的,并表明存在需要解决的问题。 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 . 如果您的传递方法不是 SpecifiedPickupDirectoryPickupDirectoryFromIis SmtpClient.SendAsync实际上只会异步发送。 In those cases it will write the message file into the pickup folder before returning. 在这些情况下,它会在返回之前将消息文件写入拾取文件夹。 Check your config file's <smtp> section. 检查配置文件的<smtp>部分。 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. 测试文件夹是否是问题根源的最佳方法是手动将电子邮件文件复制到其中。

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

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