在asp.net-mvc中,如何在不降低用户体验的情况下运行昂贵的操作?

leo*_*ora 21 c# nhibernate asp.net-mvc

我有一个asp.net-mvc网站,我正在为我的ORM使用nhibernate.

我有一个当前的控制器操作,它执行基本的CRUD更新(从数据库中查询项目,然后更新一堆值并提交回db表).然后它向客户端返回一个简单的json响应,以指示成功或错误.

 public ActionResult UpdateEntity(MyEntity newEntity)
 {
      var existingEntity = GetFromRepository(newEntity.Id);
      UpdateExistingEntity(newEntity, existingEntity);
      return Json(SuccessMessage);
 }
Run Code Online (Sandbox Code Playgroud)

在某些情况下(假设提交成功并且如果我的对象中的某些字段发生了变化)我现在想要触发一些额外的操作(比如通过电子邮件发送一堆人并运行一些生成报告的代码)但我不想放慢速度降低正在进行更新的人的用户体验.所以我担心的是,如果我这样做:

 public ActionResult UpdateEntity(MyEntity newEntity)
 {
      var existingEntity = GetFromRepository(newEntity.Id);
      bool keyFieldsHaveChanged = UpdateExistingEntity(newEntity, existingEntity);
      if (keyFieldsHaveChanged)
     {
          GenerateEmails();
          GenerateReports();
     }
    return Json(SuccessMessage);
 }
Run Code Online (Sandbox Code Playgroud)

这对于某人更新的用户体验来说太慢了.无论如何(asyngc?)有一个昂贵的操作被控制器动作触发但是没有控制器动作因此而减慢?

RPM*_*984 51

我之前做过这个.

最强大的方法是使用异步控制器,或者更好的是独立服务,例如WCF服务.

但根据我的经验,我只需要做"简单"的单行任务,如审计或报告,如你所说.

在那个例子中,简单的方法 - 触发Task:

public ActionResult Do()
{
    SomethingImportantThatNeedsToBeSynchronous();

    Task.Factory.StartNew(() => 
    {
       AuditThatTheUserShouldntCareOrWaitFor();
       SomeOtherOperationTheUserDoesntCareAbout();
    });

    return View();

}
Run Code Online (Sandbox Code Playgroud)

这是一个简单的例子.您可以根据需要启动任意数量的任务,同步它们,在完成时获得通知等.

我目前使用上面的方法来进行Amazon S3上传.

  • 将此标记为答案.RPM1884可以获得赏金 (5认同)
  • @ RPM1984 - 所以,澄清一下,你不是在等待返回视图之前完成的任务..正确?? (3认同)
  • @cpoDesign - 在我提供的例子中,你不能.但是,如我在上面的评论中所说的那样,您可以使用更多代码. (2认同)

The*_*ing 16

如果打算立即返回JSON,并让密集的工作在后台异步运行而不影响用户体验,那么您需要启动一个新的后台线程.AsyncController在这里不会帮到你.

通过执行此操作有很多关于使线程请求池挨饿的论点(这是真的),但是计数器参数是你应该饿死池,因为服务器忙于工作.理想情况下,您应该完全通过排队/分布式系统等将工作移到另一台服务器上,但这是一个复杂的解决方案.除非你需要处理数百个请求,否则你不需要考虑这个选项,因为它不太可能导致问题.这实际上取决于解决方案的可伸缩性要求,后台进程需要多长时间以及调用它的频率.最简单的解决方案如下:

public ActionResult UpdateEntity(MyEntity newEntity)
{
    var existingEntity = GetFromRepository(newEntity.Id);
    bool keyFieldsHaveChanged = UpdateExistingEntity(newEntity, existingEntity);
    if (keyFieldsHaveChanged)
    {
        ThreadPool.QueueUserWorkItem(o =>
                                        {
                                            GenerateEmails();
                                            GenerateReports();
                                        });

    }
    return Json(SuccessMessage);
}
Run Code Online (Sandbox Code Playgroud)


arc*_*hil 5

您应该执行异步操作和异步控制器以不锁定线程池,而不是使网站的其他用户受到影响.当任务运行很长时,从asp.net线程池获取的线程被保留,并且在操作完成之前不会返回到池.如果同时存在许多长时间运行的任务,那么它们将保留许多线程,因此访问您站点的其他用户很可能会因等待而受到影响.ASYNC运营不会使任何代码更快.我建议你使用异步控制器只是为了我上面写的线程案例,但这还不够.我认为你应该使用一些链接或ajax来触发服务器上的操作,让用户继续在网站上冲浪.操作完成后,在下一页刷新时,应通知用户任务已完成执行.这是另一个证明商业代码不应该写在控制器中的证明.你应该为此分开服务.