Send async e-mails

前端 未结 4 1078
一整个雨季
一整个雨季 2020-12-09 19:53

I am using ASP.NET MVC 3 with MVCMailer, I tried to send e-mails using SendAsync, but actually it still take longer.

So I am trying to use Task.Factory like the code

相关标签:
4条回答
  • 2020-12-09 20:33

    You dont need tasks. SendAsync is asynchronous and use another thread self. Tasks dont accelerate you mailing.

    UPDATE: When i resolve same problem i use task and synchronous send. It seems that SendAsync was not so asynchrous. It is sample of my code (it is not want 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;
        }
    
    0 讨论(0)
  • 2020-12-09 20:43

    Task.Factory.StartNew will create a new thread.
    If you want to access HttpContext which is in the main thread you have to do this:

    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();
    

    There a long debate if it's better to use TPL or QueueUserWorkItem.
    Someone has tried to address the issue.
    This is the QueueUserWorkItem version:

    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; }
        }
    
    }
    
    0 讨论(0)
  • 2020-12-09 20:43
    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();
            });
        }
    
    0 讨论(0)
  • 2020-12-09 20:50

    A small addition to this. Here is an extension method to help some.

    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();
           });
    }
    

    Use it like follows from your controller methods.

    Mailers.UserMailer um = new Mailers.UserMailer();
    um.SendWelcomeEmail(dataObject).SendEmailAsync(ControllerContext.HttpContext.ApplicationInstance.Context);
    
    0 讨论(0)
提交回复
热议问题