asp.net Web 服务中的异步方法

本文关键字:异步方法 服务 net Web asp | 更新日期: 2023-09-27 17:56:47

我有一个页面,可以生成一堆报告,并通过电子邮件发送结果的压缩。此过程大约需要 30 秒到 2 分钟。

当用户通过 ajax 调用此过程时,我可以通过 ajax 轮询 Web 服务以获取结果,但是如何创建一个将不断更新变量的异步方法?

为简单起见,我如何编写一个从 1 到 100 万计数并将每个数字添加到列表中的方法,同时能够轮询 Web 服务并返回该列表中的当前项目数?

任何文章也将不胜感激。

asp.net Web 服务中的异步方法

首先,一些背景思想:

1) 由于这是一个长时间运行的作业,您可能需要以前执行的作业的缓存,这样您就不会重复工作并使服务器过载。

2)您不必启动单独的后台线程,因为无论如何,每个 ASP.NET 请求都将在其自己的线程中执行。 但是,如果您发现您的 ASP.NET 工作进程正在匮乏,则可以在服务器端使 Web 方法异步 - 请参阅:本文。

该实现需要三个组件 - 缓存、用于运行作业的 Web 方法和用于检查作业状态的 Web 方法。 您还需要能够生成允许系统跟踪请求的密钥。 您可以生成此服务器端(在生成页面时)或客户端(如果您有可以生成唯一 ID 的智能算法)。

如果希望用户能够共享正在运行的作业,则可以将缓存设置为应用程序范围,

如果不希望共享,则可以将缓存设置为会话范围。 缓存只是一个字典,用于保存对正在运行的作业实例的引用。

一个简单的示例(这不会编译,可能包含语法错误,并且主要是为了表达这一点)。

服务器伪代码:

//In application Session_Start, 
Session["ReportCache"] = new Dictionary<string, ReportStatus>();
public class ReportResults
{
}
public class ReportStatus
{
    public int PercentComplete = 0;
}
[WebMethod(EnableSessions = true)]
ReportResults RunReport(string uniqueid)
{
    ReportStatus status = new ReportStatus();
    Session["ReportStatus"].Add(uniqueid, status);
    //Start report
    for(int i = 0; i < 100000; ++i)
    {
        //update the report status
        status.PercentComplete = 100 * 100000 * i / 100000;
    }
    Session["ReportStatus"].Remove(uniqueid);
}
[WebMethod(EnableSessions=true)]
public ReportStatus GetStatus(uniqueid)
{
    try
    {
        return Session["ReportStatus"][uniqueid];
    }
    catch(Exception)
    {
        return null;
    }
}

在客户端上,使用 ajax 调用第一个 Web 方法。 在报告完成之前,它不会调用 onSuccess 回调。 使用 setTimeout 定期轮询第二个 Web 方法以获取状态信息并更新客户端。 第一个 Web 方法完成后,取消计时器。

您最大的问题是您的服务器将运行大量长时间运行的作业,这可能会降低整体性能和响应能力。 您可能希望完全采用不同的方法。 例如,您可以排队想要接收报告的人员列表,而不是按用户按需运行报告 - 然后,每小时/每天/每周(无论什么)一次,您可以让一个单独的进程运行一次报告并将其发送出去。

您需要在单独的线程上运行长时间运行的进程,因此您的 Web 应用在生成这些报告时似乎不会出去吃午饭。但是,您还希望报告有关该过程进度的状态(通过 AJAX 轮询)。我建议你的 Web 服务使用一个为你处理这个问题的对象。也许是这样的:

public class ReportGenerator {
  public string CurrentStatus;
  public void Run() {
    var worker = new Thread(new ThreadStart(this.ExportReports));
    worker.Start();
  }
  private void ExportReports() {
     this.CurrentStatus = "started";
     /* export a report */
     this.CurrentStatus = "1 report complete";
     /* export another report */
     this.CurrentStatus = "2 reports complete";
     /* etc, etc, etc */
  }
}

Web 服务需要能够创建和管理此对象的实例。是否只允许在整个应用程序中一次执行其中一个导出?如果是这样,请对此对象使用单一实例模式,并确保它尚未运行,然后再重新启动它。

如果多个用户可以同时运行此过程,则必须实例化此对象并在用户的会话中存储对它的引用或类似内容,以便在轮询 Web 服务时,它可以再次获取对象并检查其状态,然后将该信息返回到客户端。使用这种类型的线程可能会遇到一些非常棘手的情况,所以要非常小心。