从线程池调用时未发送Gmail电子邮件

时间:2012-02-09 01:57:16

标签: .net asp.net-mvc email

我正在尝试以异步方式发送电子邮件,但是当我从Threadpool或简单的线程中调用我的SendMailMesage()方法时,电子邮件就不会被发送。

任何方式如何实现这一目标?定期发送电子邮件sendind会大大减慢我的申请速度。

PS:如果重要,SendMailMesage()将从引用的项目而不是MVC项目本身发送。

感谢。

修改:我的代码。

namespace App.Core.Utils.Mailer
{
    public static class Sender
    {
        /// <summary>
        /// Sends a MailMessage object using the SMTP settings.
        /// </summary>
        public static void SendMailMessage(MailMessage message)
        {
            if (!Settings.Instance.SendEmails)
            {
                return;
            }

            if (message == null)
                throw new ArgumentNullException("message");

            try
            {
                message.IsBodyHtml = true;
                message.BodyEncoding = Encoding.UTF8;
                var smtp = new SmtpClient(Settings.Instance.SmtpServer);

                // don't send credentials if a server doesn't require it,
                // linux smtp servers don't like that 
                if (!string.IsNullOrEmpty(Settings.Instance.SmtpUserName))
                {
                    smtp.Credentials = new System.Net.NetworkCredential(Settings.Instance.SmtpUserName, Settings.Instance.SmtpPassword);
                }
                smtp.Port = Settings.Instance.SmtpServerPort;
                smtp.EnableSsl = Settings.Instance.EnableSsl;
                smtp.Send(message);
            }
            catch (SmtpException)
            {
                //OnEmailFailed(message);
            }
            finally
            {
                // Remove the pointer to the message object so the GC can close the thread.
                message.Dispose();
            }
        }

        public static void SendMailMessageAsync(MailMessage message)
        {
            ThreadPool.QueueUserWorkItem(state => SendMailMessage(message));
        } 
    }
}

请注意:这在WebForms中有效。我将代码复制到MVC项目,如果我调用SendMailMessageAsync方法,现在没有任何内容被发送。没有错误。

1 个答案:

答案 0 :(得分:1)

我们在MVC中使用它,效果很好。由于它实现了一个接口,你可以将一个实例注入到Controller构造函数中,而不是使用静态方法:

public class MvcEmailSender : ISendEmails
{
    public MvcEmailSender(ILogExceptions logger, 
        IQueryEntities queryables, IUnitOfWork unitOfWork)
    {
        // save args to readonly fields on the instance
    }

    public void Send(EmailMessage message)
    {
        // constructor really passes more args here
        var sender = new SmtpEmailSender(message, arg2, arg3, arg4);
        var thread = new Thread(sender.Send);
        thread.Start();
    }
}

public class SmtpEmailSender
{
    private readonly EmailMessage _emailMessage;
    private int _retryCount;

    public SmtpEmailSender(EmailMessage emailMessage, ILogExceptions logger,
        IQueryEntities queryables, IUnitOfWork unitOfWork)
    {
        if (emailMessage == null)
            throw new ArgumentNullException("emailMessage");

        _emailMessage = emailMessage;
        // save other constructor args to readonly fields
    }

    public void Send()
    {
        try
        {
            // assemble message, send, & update database
        }
        catch (Exception ex)
        {
            // log exception, then retry like so:
            if (_retryCount++ <= 3)
            {
                Thread.Sleep(10000);
                Send();
            }
        }
    }
}

我们也在web.config中提供了一些设置,对吗?如果是这样,请使用web.config中的system.net部分更新您的问题。

要使用它,我们只需从控制器调用ISendEmails.Send(EmailMessage)。实现负责线程化,MVC不知道。

这两个类和ISendEmails接口都在引用的项目中声明,而不是MVC项目(接口在API程序集中,上面的两个类在IMPL程序集中)。

<强>更新

我已经更新了代码以回应评论。请注意,这些类的构造函数确实具有比最初描述的更多依赖性。我们(构造函数)也注入ILogExceptions,IQueryEntities和IUnitOfWork依赖项。在try块内,除了将EmailMessage转换为MailMessage并发送之外,还在数据库中更新EmailMessage域对象,并在SmtpClient.Send(MailMessage)成功后更新其SentOnUtc值。这就是我们证明电子邮件实际发送的方式。捕获异常时,会在重试之前记录该异常。