0

我想在使用 AJAX 的 ASP.NET WebForms 页面上的长时间运行任务期间提供状态更新。

有没有办法让 ScriptManager 在异步回发的同时执行和处理 Web 服务请求的脚本?

我在页面上有一个发出 Web 服务请求的脚本。它在页面加载时运行,并定期使用 setInterval()。它在异步回发启动之前正常运行,但在异步回发期间停止运行,并且在异步回发完成之前不会再次运行。

我有一个带有按钮的 UpdatePanel 来触发异步回发,该回发执行长时间运行的任务。我还有一个 AJAX WCF Web 服务的实例,它可以正常工作以获取数据并将其呈现在页面上,但是就像我说的那样,它在异步回发完成之前不会获取并呈现数据。

在异步回发期间,长时间运行的任务将更新从页面发送到 Web 服务。

问题是我可以调试并单步执行 Web 服务并查看状态更新是否已正确设置,但在异步回发完成之前,客户端脚本不会检索更新。

似乎脚本管理器正忙于执行异步回发,因此在回发完成之前它不会通过 setInterval() 运行我的其他 JavaScript。

有没有办法获取脚本管理器,或者以其他方式运行脚本以在异步回发期间从 WCF Web 服务获取数据?

我尝试了各种使用 PageRequestManager 在客户端 BeginRequest 事件上为异步回发运行脚本的方法,但它运行脚本,然后在页面请求执行时停止处理应该通过 setInterval() 运行的代码.

4

4 回答 4

0

ajax 管道可能正在排队您的请求。

尝试使用 XHR 或 jQuery 手动进行状态调用。您可能会发现这可以解决问题。

但是......请记住,一次可能发生的并发请求数量是有限的,一旦达到限制,阻塞就会开始发生。

此限制因浏览器/版本而异。

于 2010-05-22T18:51:13.153 回答
0

使用Web Developer Helper进一步检查后,似乎 Web 服务请求是以我设置的时间间隔(5 秒)发出的,但是长时间运行任务期间的第一个请求需要任务的持续时间才能返回结果,而后续请求继续不返回任何内容。任务完成后,任务开始时发送的第一个 Web 服务请求将返回状态数据。

我一直试图弄清楚为什么在任务完成之前对 Web 服务的初始请求不会返回。在 AsyncPostBack 请求结束之前,会话变量可能不会更新,所以我尝试了 ASP.NET 缓存,但这也不起作用。我在 InstanceContextMode.PerSession 和 InstanceContextMode.Single 模式下尝试了使用 Web 服务的局部变量。

我一直在关注MSDN Mag 中的示例:2007 年 7 月:Cutting Edge,但使用 ASP.NET 缓存似乎对 AsyncPostBack 没有帮助。我将尝试在我的代码隐藏中直接调用 WebMethod 方法而不是 AsyncPostBack,但文章说它应该可以工作,所以我想弄清楚为什么我的实现没有。

所以:

  1. 页面加载。
  2. setInterval('getUpdate()', 5000) 开始。
  3. getUpdate() 调用 Web 服务并每 5 秒返回一次 emtpy 数据。
  4. 用户单击按钮以在 UpdatePanel 中启动 AsyncPostBack。
  5. 服务器端处理从长时间运行的任务开始。
  6. getUpdate() 调用 Web 服务。请求待处理。
  7. 长期运行的任务继续进行。
  8. getUpdate() 继续每 5 秒调用一次 Web 服务。每个请求都返回空数据。
  9. 长时间运行的任务完成。
  10. AsyncPostBack 完成并向浏览器返回响应。
  11. 未完成的 Web 服务请求将被发送响应。
  12. getUpdate() 返回 Web 服务响应并更新页面以显示结果。
于 2010-05-24T23:56:12.317 回答
0

为将来可能会发现此问题的任何人提供解决方案的后续行动。

我尝试以多种方式发送请求:

  • 当您将 WCF 服务作为 ServiceReference 添加到 ScriptManager 时创建的 AJAX 脚本对象。因此,如果 WCF 服务类是带有 GetProgress 方法的 ProgressService,我在 JavaScript 中创建了一个新的 ProgressService 对象并调用 progressService.GetProgress()。
  • 一个 XmlHttpRequest 请求。
  • 一个 jQuery $.getJson() 请求。
  • 一个 Sys.Net.WebRequest 请求。
  • Sys.Net.WebServiceProxy 请求。

事实证明,即使发送了客户端请求,即没有被 ASP.NET ScriptManager 缓冲,如果 WCF 服务是 IIS 中同一网站的一部分,它也不会响应。

因此,我没有创建一个完全独立的 WCF 项目和一个完全独立的 IIS 网站,而是切换到了传统的 ASP.NET (SOAP) Web 服务 (.asmx)。

我能够在 Visual Studio 中保留同一项目的 .asmx 服务部分,并在 IIS 中保留网站。请求在回发期间发送,服务在回发期间做出响应。

在 ScriptManager 下将其添加为 ServiceReference 之后,我还能够使用基本相同的脚本对象,创建一个新的 ProgressWebService(),然后调用 progressWebService.GetProgress()。传递给 GetProgress() 的回调处理程序然后处理响应并更新 UI。

网络服务:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Caching;

namespace MyNamespace
{
    public class Progress
    {
        public string Message { get; set; }
        public bool Complete { get; set; }
    }

    [WebService(Namespace = "MyNamespace")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    [System.Web.Script.Services.ScriptService]
    public class ProgressWebService : System.Web.Services.WebService
    {
        protected static Dictionary<string, Progress> ProgressMessages = new Dictionary<string, Progress>();

        [WebMethod]
        public Progress GetProgress(string progressId)
        {
            return ProgressMessages.ContainsKey(progressId) ? ProgressMessages[progressId] : new Progress();
        }

        [WebMethod]
        public void SetProgress(string progressId, string progress, bool complete)
        {
            if (ProgressMessages.ContainsKey(progressId))
            {
                ProgressMessages[progressId].Message = progress;
                ProgressMessages[progressId].Complete = complete;
            }
            else
                ProgressMessages.Add(progressId, new Progress() { Message = progress, Complete = complete });
        }

        [WebMethod]
        public void SetProgressComplete(string progressId, bool complete)
        {
            if (ProgressMessages.ContainsKey(progressId))
                ProgressMessages[progressId].Complete = complete;
            else
                ProgressMessages.Add(progressId, new Progress() { Complete = complete });
        }

        [WebMethod]
        public void AddProgress(string progressId, string progress)
        {
            if (ProgressMessages.ContainsKey(progressId))
                ProgressMessages[progressId].Message += progress;
            else
                ProgressMessages.Add(progressId, new Progress() { Message = progress });
        }
    }
}

客户端:

<%@ Page language="c#" CodeFile="About.aspx.cs" Inherits="MyNamespace.About" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <script type="text/javascript">
        var ProgressServiceInterval; // global interval var, so it can be set and cleared across functions

        function btnBackup_Click(sender, e) {
            sender.disabled = true; // disable the backup button, so the request isn't duplicated
            // start getting the backup progress from the web service
            var progressService = new MyNamespace.ProgressWebService();
            progressService.SetProgressComplete('<%= strBackupProgressGuid %>', false, null, null, null);
            ProgressServiceInterval = setInterval('setBackupProgress()', 1000); // get progress once per second
        }

        function setBackupProgress() {
            var progressService = new MyNamespace.ProgressWebService();
            progressService.GetProgress('<%= strBackupProgressGuid %>', progressCallback, null, null);
        }

        function progressCallback(result) {
            var txtBackupOutput = $get('<%= txtBackupOutput.ClientID %>');
            try {
                // show the progress message
                txtBackupOutput.value = result.Message; 
                // stop checking if progress is complete
                if (result.Complete == true) clearInterval(ProgressServiceInterval);
                // scroll the textarea to the bottom
                txtBackupOutput.scrollTop = txtBackupOutput.scrollHeight - txtBackupOutput.clientHeight;
            } catch (ex) {

            }
        }
    </script>         
</head>
<body>
    <form id="frmMyForm" method="post" runat="server">
        <ajaxToolkit:ToolkitScriptManager runat="Server" EnablePartialRendering="true" EnablePageMethods="true" ID="ScriptManager1" >
            <Services>
                <asp:ServiceReference Path="ProgressWebService.asmx" />
            </Services>
        </ajaxToolkit:ToolkitScriptManager>

        <asp:UpdatePanel ID="updBackup" runat="server" RenderMode="Inline">
            <ContentTemplate>
                <asp:UpdateProgress ID="updBackupProgress" AssociatedUpdatePanelID="updBackup" runat="server" DynamicLayout="false">
                    <ProgressTemplate>
                        <div style="text-align:center;margin-bottom:-32px;"> 
                            <img src="loading.gif" alt="Loading..." />
                        </div>
                    </ProgressTemplate>
                </asp:UpdateProgress>

                <asp:Button ID="btnBackup" runat="server" CssClass="SubmitButton" Text="Back Up Data" UseSubmitBehavior="false" OnClientClick="btnBackup_Click(this, event);" />
                <br /><br />

                <asp:TextBox ID="txtBackupOutput" runat="server" ReadOnly="true" TextMode="MultiLine" Rows="10" Width="100%" Wrap="true" />  

            </ContentTemplate>
        </asp:UpdatePanel>
    </form>
</body>
</html>

和服务器端:

namespace MyNamespace
{
    public partial class About
    {
        protected string strBackupProgressGuid
        {
            get
            {
                if (Session["strBackupProgressGuid"] == null)
                    Session["strBackupProgressGuid"] = Guid.NewGuid().ToString();
                return Session["strBackupProgressGuid"] as string;
            }
        }

        ProgressWebService _progressService;
        protected ProgressWebService progressService
        {
            get
            {
                return _progressService = _progressService ?? new ProgressWebService();
            }
        }

       void btnBackup_Click(object sender, EventArgs e)
        {
            progressService.SetProgress(strBackupProgressGuid, "Started\r\n", false);
            System.Threading.Thread.Sleep(10000);
            progressService.AddProgress(strBackupProgressGuid, "+10\r\n");
            System.Threading.Thread.Sleep(10000);
            progressService.AddProgress(strBackupProgressGuid, "+20\r\n");
            System.Threading.Thread.Sleep(10000);
            progressService.AddProgress(strBackupProgressGuid, "+30\r\n");

            progressService.SetProgressComplete(strBackupProgressGuid, true);
        }
    }   
}
于 2010-08-11T20:23:06.677 回答
0

把它放在你的脚本管理器之后。

<script type="text/javascript">
        var prm = Sys.WebForms.PageRequestManager.getInstance();
        prm.add_initializeRequest(InitializeRequestHandler);
        prm.add_endRequest(EndRequestHandler);

        var pbQueue = new Array();
        var argsQueue = new Array();

        function InitializeRequestHandler(sender, args) {
            if (prm.get_isInAsyncPostBack()) {
                args.set_cancel(true);
                pbQueue.push(args.get_postBackElement().id);
                argsQueue.push(document.forms[0].__EVENTARGUMENT.value);
            }
        }

        function EndRequestHandler(sender, args) {
            if (pbQueue.length > 0) {
                __doPostBack(pbQueue.shift(), argsQueue.shift());
            }
        }
    </script>
于 2011-07-15T13:38:00.840 回答