Отправка писем выполняется асинхронно после возвращения результата просмотра

1

У меня есть следующий метод контроллера

public async Task<ActionResult> SendToAllUsers(SentMailToAllUsersModel model)
    {
        if (ModelState.IsValid)
        {
            var mail = MailService.SendMailToAllUsers(model.Body, model.Title);
            await mail;
        }

        return View(model);
    }

Который вызывает этот метод в почтовой службе

public Task SendMailToAllUsers(string content, string title)
    {

        var users = UserService.GetAllUsers();
        var mailTemplates =  users.Result.AsParallel().Select(user =>
        {

            var mailTemplate = new MastersMailTemplate(user);
            mailTemplate.HtmlEmailTemplate = content;
            mailTemplate.Subject = title;
            mailTemplate.From = _fromEmail;

            return Task.Factory.StartNew(() => MailProvider.SendEmailAsync(mailTemplate.CreateMailMessage(), new ResultDescription()).ConfigureAwait(false));
        }).ToArray();

        return Task.WhenAll(mailTemplates);




    }

Этот метод запускает почтовый провайдер, который выполняет этот метод:

public Task<IResultDescription> SendEmailAsync(MailMessage message, IResultDescription rd)
    {
        // Create our SMTP Client
        SmtpClient client = new SmtpClient();
        client.Host = SmtpServer;
        client.Port = SmtpServerPort;
        client.Credentials = new NetworkCredential(SmtpServerUsername, SmtpServerPassword);
        client.EnableSsl = true;

        if (AppSettings.IsInTestMode)
        {

            Log.Info("Test mode check: Removing all emails and replace to test");
            message.To.Clear();
            foreach (var email in AppSettings.DefaultTestEmail)
            {
                message.To.Add(new MailAddress(email));
            }
        }

        client.Timeout = 10;
        Log.Info("Sending Email to" + message.To.FirstOrDefault());
        var task = Task.Run(async () =>
        {
            try{
                client.SendCompleted += (s, e) =>
                {
                    client.Dispose();
                    message.Dispose();
                };
                await client.SendAsync(message);
                rd.Success = true;
                return rd;
            }
            catch (Exception e)
            {
                Log.Error("Email not send");
                rd.Success = false;
                if (rd.Errors == null)
                {

                    IList<string> errors = new List<string>();
                    errors.Add(e.Message);
                    rd.Errors = errors;
                }
                else
                {
                    rd.Errors.Add(e.Message);
                }

                return rd;
            }
        });

        return task;




    }

Проблема заключается в том, что представление результата возвращается перед любыми отправляемыми сообщениями.
Контроллер не будет ждать, пока все письма не будут отправлены.

Как я могу убедиться, что контроллер продолжает выполнение только после завершения всех заданий в почтовой службе?

Теги:
email
asynchronous

2 ответа

2

Как правило, не используйте Task.Run, Task.Factory.StartNew, Parallel или PLINQ на ASP.NET. Всегда есть лучший способ. В этом случае просто используйте async и await:

public async Task SendMailToAllUsersAsync(string content, string title)
{
  var users = await UserService.GetAllUsersAsync();
  var mailTemplates = users.AsParallel().Select(user =>
  {
    var mailTemplate = new MastersMailTemplate(user);
    mailTemplate.HtmlEmailTemplate = content;
    mailTemplate.Subject = title;
    mailTemplate.From = _fromEmail;
    return MailProvider.SendEmailAsync(mailTemplate.CreateMailMessage());
  }).ToArray();
  return await Task.WhenAll(mailTemplates);
}

Аналогично вашему внутреннему методу:

public Task<IResultDescription> SendEmailAsync(MailMessage message, IResultDescription rd)
{
  using (SmtpClient client = new SmtpClient())
  using (message)
  {
    client.Host = SmtpServer;
    client.Port = SmtpServerPort;
    client.Credentials = new NetworkCredential(SmtpServerUsername, SmtpServerPassword);
    client.EnableSsl = true;

    if (AppSettings.IsInTestMode)
    {
      Log.Info("Test mode check: Removing all emails and replace to test");
      message.To.Clear();
      foreach (var email in AppSettings.DefaultTestEmail)
      {
        message.To.Add(new MailAddress(email));
      }
    }

    client.Timeout = 10;
    Log.Info("Sending Email to" + message.To.FirstOrDefault());
    try
    {
      await client.SendAsync(message);
      rd.Success = true;
    }
    catch (Exception e)
    {
      Log.Error("Email not send");
      rd.Success = false;
      if (rd.Errors == null)
      {
        IList<string> errors = new List<string>();
        errors.Add(e.Message);
        rd.Errors = errors;
      }
      else
      {
        rd.Errors.Add(e.Message);
      }
    }
    return rd;
  }
}

Помните, async упрощает async. Если async код чрезмерно сложный, проверьте "Лучше". У меня есть async введение в мой блог, который может оказаться полезным.

  • 1
    Привет, сэр, извините за возрождение этой темы. Но у меня проблема. Когда я работал, моя программа localhost работает нормально, но когда я размещал свой веб-сайт, почта отправлялась, но SendMailAsync никогда не возвращался. Это тупиковая проблема? Я просто новичок в таких вещах, надеюсь, вы поможете мне, сэр, заранее спасибо. Вот мой контроллер: pastie.org/10907763 Пытался редактировать await smtp.SendMailAsync(message) для await smtp.SendMailAsync(message).ConfigureAwait(false); но все равно это никогда не вернется. Очень надеюсь, что вы можете помочь мне, сэр. Спасибо. Удачи. :)
  • 0
    Я просмотрел код, и ничего не выпрыгнуло на меня. Нужно проверить, что вы нацеливаетесь на .NET 4.5 или выше, и для targetFramework установлено targetFramework 4.5 или выше в вашем web.config . Сам код выглядит нормально и не должен вызывать тупик. Если это не проблема с таргетингом на 4,5, то я рекомендую уменьшить проблему до минимального количества кода, необходимого для ее воспроизведения, и опубликовать его как собственный вопрос.
0

Я думаю, проблема в вашем методе SendMailToAllUsers. Я думаю, вам нужно await вызова MailProvider.SendEmailAsync. Если вы этого не сделаете, задача, начатая Task.Factory.StartNew будет считаться завершенной, как только этот метод будет выполнен. Поскольку метод на самом деле асинхронный, он только начинает работу, он не ждет его завершения. Если вы await результата, который должен устранить проблему.

Измените свой код на:

public Task SendMailToAllUsers(string content, string title)
    {
        var users = UserService.GetAllUsers();
        var mailTemplates =  users.Result.AsParallel().Select(user =>
        {

            var mailTemplate = new MastersMailTemplate(user);
            mailTemplate.HtmlEmailTemplate = content;
            mailTemplate.Subject = title;
            mailTemplate.From = _fromEmail;

            // Await the result of the lambda expression
            return Task.Factory.StartNew(() => await MailProvider.SendEmailAsync(mailTemplate.CreateMailMessage(), new ResultDescription()).ConfigureAwait(false));
        }).ToArray();

        return Task.WhenAll(mailTemplates);
    }

Ещё вопросы

Сообщество Overcoder
Наверх
Меню