Отправляйте асинхронные электронные письма

Я использую ASP.NET MVC 3 с MVCMailer, я пытался отправлять электронные письма с помощью SendAsync, но на самом деле это все еще занимает больше времени.

Итак, я пытаюсь использовать Task.Factory, как показано ниже:

   var task1 = Task.Factory.StartNew(
            state =>
            {
                var mail = new UserMailer();
                var msg = mail.Welcome("My Name", "[email protected]");
                msg.SendAsync(); 
            });

   task1.Wait();

Проблема в том, что MVCMailer нужен HttpContext, но внутри этой задачи я получил HttpContext Null.

Как я могу отправлять асинхронные электронные письма?


person Michel Andrade    schedule 30.08.2012    source источник


Ответы (4)


Небольшое дополнение к этому. Вот метод расширения, чтобы помочь некоторым.

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);
person Matt    schedule 03.03.2013
comment
Эй, Мэтт! спасибо за ссылку, это выглядит великолепно, он отправляет его асинхронно на 100%, я слышал, что люди говорят, что MVCMailer был псевдоасинхронным, ха-ха. Спасибо друг! - person williamsandonz; 04.03.2013
comment
Нет проблем! Я немного побивался головой о стол с этим :) - person Matt; 04.03.2013
comment
Наличие HttpContext в двух потоках одновременно опасно и не поддерживается ASP.NET. - person Stephen Cleary; 05.12.2013

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", "[email protected]");
    msg.SendAsync();
    });

task1.Wait();

Идет долгий спор о том, что лучше использовать: TPL или QueueUserWorkItem.
Кто-то пытался обратиться к проблема.
Это версия 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; }
    }

}
person LeftyX    schedule 30.08.2012
comment
Да, Лефлайкс! Я уже пробовал это, но не знаю, можно ли передать переменную контекста объекту MVCMailer. - person Michel Andrade; 31.08.2012
comment
@MichelAndrade: я обновил свой ответ. Попробуйте посмотреть, поможет ли это. - person LeftyX; 31.08.2012
comment
LeflyX, Почти готово, если я сниму task1.Wait(); это не работает - person Michel Andrade; 31.08.2012
comment
@MichelAndrade: Мне подходит. Вы создаете новую задачу в цикле? Я бы посоветовал вам прочитать это: codethinked.com/net-40-and-systemthreadingtasks< /а> - person LeftyX; 31.08.2012
comment
@LeflyX: Не могли бы вы взглянуть на мой код? codepaste.net/7acz6u Я не использую асинхронное управление, в чем проблема? - person Michel Andrade; 31.08.2012
comment
Нет проблем, извините, что утомил вас. Спасибо за вашу помощь. - person Michel Andrade; 03.09.2012
comment
@MichelAndrade: Нет проблем. На 50% ваших вопросов ответы не принимаются. Помогайте другим иногда. - person LeftyX; 03.09.2012
comment
Наличие HttpContext в двух потоках одновременно опасно и не поддерживается ASP.NET. - person Stephen Cleary; 05.12.2013

Вам не нужны задачи. 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;
    }
person Kirill Bestemyanov    schedule 30.08.2012
comment
Кирилл, а почему так долго? Мне нужно отправить электронное письмо в фоновом режиме. - person Michel Andrade; 30.08.2012
comment
Асинхронный не значит быстрый. Это означает, что ваш медленный процесс не будет блокировать ваш базовый поток, и ваше приложение будет реагировать на действия пользователя. - person Kirill Bestemyanov; 30.08.2012
comment
Конечно, но когда я отправляю электронные письма, процесс блокируется этой задачей. - person Michel Andrade; 30.08.2012
comment
Кстати, я нашел свой собственный код для решения той же проблемы, я тоже использовал задачи. Я обновляю свой ответ образцом своего кода (HttpContext не нужен) - person Kirill Bestemyanov; 30.08.2012

person    schedule
comment
Спасибо за публикацию ответа! Хотя фрагмент кода может ответить на вопрос, все же полезно добавить некоторую дополнительную информацию, например объяснение и т. д. - person j0k; 06.10.2012