【问题标题】:Send async e-mails发送异步电子邮件
【发布时间】:2012-08-30 16:23:56
【问题描述】:

我正在使用带有 MVCMailer 的 ASP.NET MVC 3,我尝试使用 SendAsync 发送电子邮件,但实际上仍然需要更长的时间。

所以我尝试像下面的代码一样使用 Task.Factory:

   var task1 = Task.Factory.StartNew(
            state =>
            {
                var mail = new UserMailer();
                var msg = mail.Welcome("My Name", "myemail@gmail.com");
                msg.SendAsync(); 
            });

   task1.Wait();

问题是,MVCMailer 需要 HttpContext,但在这个任务中我得到了 HttpContext Null。

如何发送异步电子邮件?

【问题讨论】:

    标签: c# asp.net-mvc mvcmailer


    【解决方案1】:

    对此的一个小补充。这是一个可以帮助一些人的扩展方法。

    using Mvc.Mailer; 
    using System.Threading.Tasks;
    public static void SendEmailAsync(this MvcMailMessage msg, HttpContext currContext)
    {
          //make this process a little cleaner
          Task.Factory.StartNew(() =>
          {
               System.Web.HttpContext.Current = currContext;
               msg.SendAsync();
           });
    }
    

    从你的控制器方法中使用它。

    Mailers.UserMailer um = new Mailers.UserMailer();
    um.SendWelcomeEmail(dataObject).SendEmailAsync(ControllerContext.HttpContext.ApplicationInstance.Context);
    

    【讨论】:

    • 嗨,马特!感谢您的参考,这看起来很棒,它是否 100% 异步发送,我听说有人说 MVCMailer 是伪异步哈哈。谢谢老哥!
    • 没有问题!我用这个把头撞在桌子上一会儿:)
    • 在两个线程上同时拥有HttpContext 是危险的,并且不受 ASP.NET 的支持。
    【解决方案2】:

    Task.Factory.StartNew 将创建一个新线程。
    如果你想访问主线程中的 HttpContext,你必须这样做:

    var task1 = Task.Factory.StartNew(() =>
        {
        System.Web.HttpContext.Current = ControllerContext.HttpContext.ApplicationInstance.Context;
        var mail = new UserMailer();
        var msg = mail.Welcome("My Name", "myemail@gmail.com");
        msg.SendAsync();
        });
    
    task1.Wait();
    

    使用 TPL 或 QueueUserWorkItem 是否更好存在长期争论。
    有人试图联系issue
    这是 QueueUserWorkItem 版本:

    public class HomeController : Controller
    {
        private AutoResetEvent s_reset = new AutoResetEvent(false);
    
        public ActionResult Index()
        {
            var state = new WorkerState() { HttpContextReference = System.Web.HttpContext.Current };
            ThreadPool.QueueUserWorkItem(new WaitCallback(EmaiSenderWorker), state);
    
            try
            {
            s_reset.WaitOne();
            }
            finally
            {
            s_reset.Close();
            }
    
            return View();
        }
    
        void EmaiSenderWorker(object state)
        {
            var mystate = state as WorkerState;
    
            if (mystate != null && mystate.HttpContextReference != null)
            {
            System.Web.HttpContext.Current = mystate.HttpContextReference;
            }
    
            var mail = new UserMailer();
            var msg = mail.Welcome();
            msg.SendAsync();
    
            s_reset.Set();
        }
    
        private class WorkerState
        {
            public HttpContext HttpContextReference { get; set; }
        }
    
    }
    

    【讨论】:

    • 是的 LeflyX!我已经尝试过了,但我不知道是否可以将上下文变量传递给 MVCMailer 对象。
    • @MichelAndrade:我已经更新了我的答案。试试看是否有帮助。
    • LeflyX,快到了,如果我取消“task1.Wait();”它不起作用
    • @MichelAndrade:对我有用。您是否在循环中创建新任务?我建议您阅读以下内容:codethinked.com/net-40-and-systemthreadingtasks
    • @LeflyX:你介意看看我的代码吗? codepaste.net/7acz6u我没有使用Async Control,有什么问题吗?
    【解决方案3】:

    你不需要任务。 SendAsync 是异步的并使用另一个线程自身。任务不会加快您的邮件发送速度。

    更新: 当我解决同样的问题时,我使用任务和同步发送。看来 SendAsync 不是那么异步。这是我的代码示例(它不需要 HttpContext):

    public void SendMailCollection(IEnumerable<Tuple<string, string, MailAddress>> mailParams)
        {
            var smtpClient = new SmtpClient
            {
                Credentials = new NetworkCredential(_configurationService.SmtpUser, _configurationService.SmtpPassword),
                Host = _configurationService.SmtpHost,
                Port = _configurationService.SmtpPort.Value
            };
    
            var task = new Task(() =>
                                    {
                                        foreach (MailMessage message in mailParams.Select(FormMessage))
                                        {
                                            smtpClient.Send(message);
                                        }
    
                                    });
            task.Start();
        }
    
        private MailMessage FormMessage(Tuple<string, string, MailAddress> firstMail)
        {
            var message = new MailMessage
                {
                    From = new MailAddress(_configurationService.SmtpSenderEmail, _configurationService.SmtpSenderName),
                    Subject = firstMail.Item1,
                    Body = firstMail.Item2
                };
            message.To.Add(firstMail.Item3);
            return message;
        }
    

    【讨论】:

    • 基里尔,为什么需要更长的时间?我需要在后台发送电子邮件。
    • 异步并不意味着快。这意味着您的缓慢进程不会阻塞您的基础线程,并且您的应用程序将对用户操作做出反应
    • 当然,但是当我发送电子邮件时,该过程被此任务阻止。
    • 顺便说一句,我找到了自己的代码来解决同样的问题,我也使用了任务。我用我的代码示例更新我的答案(它不需要 HttpContext)
    【解决方案4】:
    public class UserMailer : MailerBase    
    {
        RegisterService Service = new RegisterService();
        HttpContext Context;
        public UserMailer(HttpContext context)
        {
            Context = context;
            MasterName="_Layout";
        }
    
        public void ConfirmRegistration(Register model)
        {
            SendAsync(() =>
            {
                model.Conference = Service.Context.Conferences.Find(model.ConferenceID); // load conference bcs it fails to lazy load automatically.
                ViewData.Model = model;
                return Populate(x =>
                {
                    x.Subject = "You registered for " + model.Conference.Name + "!"; ;
                    x.ViewName = "Confirm";
                    x.To.Add(model.Email);
                });
            });
        }
    
        private void SendAsync(Func<MvcMailMessage> GetEmail)
        {
            Task.Factory.StartNew(() =>
            {
                System.Web.HttpContext.Current = Context;
                GetEmail().Send();
            });
        }
    

    【讨论】:

    • 感谢您发布答案!虽然代码 sn-p 可以回答这个问题,但添加一些附加信息仍然很棒,比如解释等..
    猜你喜欢
    • 2011-05-06
    • 2017-07-13
    • 2011-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多