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

时间:2012-01-07 10:04:40

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

这有一半的时间。另一半我会在CallBack中收到“无法访问已处置对象”的错误。

下一个实施(来自具有良好声誉的成员: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("myaddress@gmail.com", "mypassword");

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

使用这个实现我没有得到任何错误,但是当执行smtpClient.SendAsync()时调试模式的延迟明显更长(约5秒),这让我认为它不是异步发送的。

问题:

1)导致“处置对象”错误的第一种方法出了什么问题?

2)第二个实现是否存在导致电子邮件不能异步发送的问题? 5秒延迟是无意义的吗?

可能还需要注意的是,尽管该网站不需要支持发送大量电子邮件(仅限用户注册,选择加入新闻通讯等),但我们预计将来会有大量用户,因此我决定异步发送电子邮件。

感谢。

3 个答案:

答案 0 :(得分:7)

由于USING-block,你的第一种方法无法正常工作。使用块结束后,将释放SmtpClient对象。因此,您无法在事件处理程序中访问它。

答案 1 :(得分:5)

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

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

为smtpClient对象设置了3个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;
    }

答案 2 :(得分:1)

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

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

如果您的投放方式不是 SmtpClient.SendAsyncSpecifiedPickupDirectory

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