执行后端电子邮件处理而无需让用户等待其完成。

3
在我的MVC应用程序中,我有一个功能是让客户向各自的所有者/经销商发送消息。消息模型如下:
public class Messages
{
   public string MessageFrom { get; set; }
   public string MessageUserEmailID { get; set; }
   public string MessageUserContactNum { get; set; }
   public string Message { get; set; }
   public int UserID { get; set; }//Admin user id
   public string propertyName { get; set; }
}

以下是我的Controller方法,将通过AJAX调用:

  • 我会将消息存储在数据库中
  • 发送电子邮件通知管理员
  • 向用户返回一个Json响应。

HomeController

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SendMessage([Bind(Prefix = "messageModel")]Messages model)
{
     private SendNotification notify = new SendNotification();
     if (ModelState.IsValid)
     {
          using (db)
          {
             tblMessage messageModel = new tblMessage();
             //....
             //....
             //....
             //fill the messageModel with the model values
             db.tblMessages.Add(messageModel);
             db.SaveChanges();
             string toAddress = db.tblUsers.Where(u => u.UserId == model.UserID).Select(x => x.UserEmailID).FirstOrDefault();
             if (!string.IsNullOrEmpty(toAddress))
             {
                  notify.NotifyUser(toAddress, model.MessageUserContactNum, model.propertyName, model.Message, model.MessageFrom, model.MessageUserEmailID, "Kemmale Engineers' Notification - You have a message");
             }
             return Json(new { result = true, message = "Message sent! Thank you! We will get back to you soon!" }, JsonRequestBehavior.AllowGet);
         }
    }
    return Json(new { result = false, message = "Server issue! Please try again later" }, JsonRequestBehavior.AllowGet);
}

SendNotification 类包含以下 NotifyUser 方法:

public void NotifyUser(string toAddress, string userPhoneNum, string propertyName, string userMessage, string userName, string userContactEmail, string subject)
{
    using (MailMessage mail = new MailMessage())
    {
        string Password = WebConfigurationManager.AppSettings["ApplicationEmailPassword"];
        var securePassword = new SecureString();
        Array.ForEach(Password.ToArray(), securePassword.AppendChar);
        securePassword.MakeReadOnly();
        mail.To.Add(toAddress);
        mail.From = new MailAddress(WebConfigurationManager.AppSettings["ApplicationEmailID"]);
        mail.Subject = subject;
        mail.Body = Convert.ToString(ConstructBody(userMessage, propertyName, userPhoneNum, userContactEmail, userName));
        mail.IsBodyHtml = true;
        mail.Priority = MailPriority.High;
        SmtpClient smtp = new SmtpClient();
        smtp.Host = "smtp.gmail.com";
        smtp.Port = 587;
        smtp.UseDefaultCredentials = false;
        smtp.EnableSsl = true;
        smtp.Credentials = new NetworkCredential(Convert.ToString(mail.From), securePassword);
        smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
        smtp.Send(mail);
    }
}

您可以看到,Notify的返回类型是void。现在我该如何在不等待Notify方法执行的情况下向用户返回Json消息呢?因为让用户等待与他无关的内部过程完成非常过时。我正在考虑使用异步任务,但不确定如何在这里实现。


Task.Run(() => notify.NotifyUser(...));的翻译: - Gusman
@Gusman.. 你能否详细说明一下? - Guruprasad J Rao
嗯...不用详细解释,只需将notify.NotifyUser的调用用lambda包围起来,并将其传递给Task.Run,它将在另一个线程中运行,而不必等待邮件发送的响应即可立即返回。 - Gusman
@Gusman。谢谢。我已经实现了,但由于我使用的是.net framework 4,所以我不得不使用Task.Factory.StartNew(() => {NotifyUser();});。您可以将其添加为答案,这样我就可以接受它了。 - Guruprasad J Rao
1个回答

3

如果你想执行一个函数,而不想等它执行完,那么你必须在另一个线程中运行它。

最简单的方法是使用任务功能。

在FW4.5或更高版本中,可以使用

Task.Run(ActionToExecute)

如果您使用的是FW4.0,可以使用以下方法:
Task.Factory.StartNew(ActionToExecute).

否则,您可以使用线程池的传统方法来完成:
ThreadPool.EnqueueUserWorkItem(ActionToExecute, null);

但是在这种情况下,动作必须有一个对象参数(即使被忽略)。


1
在ASP.NET中使用Task.Run是一个糟糕的想法。说这样做不会对性能产生影响是不正确的,因为你基本上需要双倍的线程来处理单个请求。 - Yuval Itzchakov
1
线程池将处理运行多少工作线程,因此完全没有问题,也没有性能影响,您不会使线程计数翻倍,如果达到线程池限制,它将排队任务,直到有任何线程被释放。 - Gusman
1
如果你强制回收应用程序池,即使是正常调用,任何长时间运行的任务都将被取消... - Gusman
1
真的吗?你是认真的吗?如果我们这样做,使用线程池将增加任何具有多个核心的计算机的性能,因此,您会失去一些RAM KB,但会获得速度.. - Gusman
1
我并没有说更多的线程就是更好的。正如我所说,如果没有空闲线程,ThreadPool会将任务排队。 - Gusman
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接