返回视图结果后发送邮件异步执行

时间:2014-05-17 10:11:24

标签: c# email asynchronous

我有以下控制器方法

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;




    }

问题是在发送的任何邮件之前返回结果视图 控制器没有等待,直到所有邮件都被发送。

如何在邮件服务中的所有任务完成后确保控制器仅继续执行?

2 个答案:

答案 0 :(得分:2)

作为一般规则,请勿在ASP.NET上使用Task.RunTask.Factory.StartNewParallel或PLINQ。总有一种更好的方法。在这种情况下,只需使用asyncawait

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 intro您可能会觉得有用。

答案 1 :(得分: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);
    }