Выполнение одновременного запроса веб-службы AJAX WCF во время асинхронной обратной передачи - PullRequest
0 голосов
/ 22 мая 2010

Я хочу предоставлять обновления статуса во время длительной задачи на странице веб-форм ASP.NET с AJAX.

Есть ли способ заставить ScriptManager выполнять и обрабатывать сценарий для запроса веб-службы одновременно с асинхронной обратной передачей?

У меня есть скрипт на странице, который делает запрос веб-службы. Он запускается при загрузке страницы и периодически использует setInterval (). Он работает правильно до инициации асинхронной обратной передачи, но останавливается во время асинхронной обратной передачи и не запускается до тех пор, пока не завершится асинхронная обратная передача.

У меня есть UpdatePanel с кнопкой для запуска асинхронной обратной передачи, которая выполняет долгосрочную задачу. У меня также есть экземпляр веб-службы AJAX WCF, которая работает правильно для извлечения данных и представления их на странице, но, как я уже сказал, она не извлекает и не представляет данные, пока не завершится асинхронная обратная передача.

Во время асинхронной обратной передачи длительная задача отправляет обновления со страницы в веб-службу.

Проблема в том, что я могу отлаживать и проходить через веб-сервис и видеть, что обновления состояния установлены правильно, но обновления не извлекаются клиентским сценарием до тех пор, пока не завершится асинхронная обратная передача.

Кажется, диспетчер сценариев занят выполнением асинхронной обратной передачи, поэтому он не запускает мой другой JavaScript с помощью setInterval () до тех пор, пока обратная передача не завершится.

Есть ли способ заставить диспетчер сценариев или иным образом запустить сценарий для извлечения данных из веб-службы WCF во время асинхронной обратной передачи?

Я пробовал различные методы использования PageRequestManager для запуска сценария на клиентском событии BeginRequest для асинхронной обратной передачи, но он запускает сценарий, а затем останавливает обработку кода, который должен выполняться с помощью setInterval (), в то время как выполняется запрос страницы.

Ответы [ 4 ]

0 голосов
/ 15 июля 2011

Добавьте это после вашего менеджера сценариев.

<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>
0 голосов
/ 25 мая 2010

При дальнейшей проверке с помощью Помощник веб-разработчика выясняется, что запросы веб-службы выполняются с установленным мною интервалом (5 секунд), но первый запрос во время длительной задачи занимает время задачи вернуть результат, в то время как последующие запросы продолжают ничего не возвращать. По завершении задачи первый запрос веб-службы, отправленный при ее запуске, возвращается с данными о состоянии.

Я пытался выяснить, почему первоначальный запрос к веб-сервису не возвращается, пока задача не завершится. Переменные сеанса могут не обновляться до конца запроса AsyncPostBack, поэтому я попробовал кеш ASP.NET, но это тоже не работает. Я попытался использовать локальную переменную с помощью веб-службы в режимах InstanceContextMode.PerSession и InstanceContextMode.Single.

Я следовал примеру MSDN Mag: июль 2007 г .: ультрасовременный , но использование кэша ASP.NET, похоже, не помогло с AsyncPostBack. Я собираюсь попробовать вызывать методы WebMethod в моем коде позади непосредственно вместо AsyncPostBack, но в статьях говорится, что это должно работать, поэтому я хотел бы выяснить, почему моя реализация не работает.

Итак:

  1. Страница загружается.
  2. setInterval ('getUpdate ()', 5000) запускается.
  3. getUpdate () вызывает веб-сервис и возвращает данные emtpy каждые 5 секунд.
  4. Пользователь нажимает кнопку, чтобы запустить AsyncPostBack в панели обновления.
  5. Обработка на стороне сервера начинается при длительном задании.
  6. getUpdate () вызывает веб-сервис. Запрос находится на рассмотрении.
  7. Долгосрочная задача продолжается.
  8. getUpdate () продолжает вызывать веб-сервис каждые 5 секунд. Каждый запрос возвращает пустые данные.
  9. Долгосрочная задача завершена.
  10. AsyncPostBack завершает работу и возвращает ответ браузеру.
  11. На ожидающий запрос веб-сервиса отправляется ответ.
  12. getUpdate () возвращает ответ веб-службы и обновляет страницу для отображения результата.
0 голосов
/ 12 августа 2010

Последующее решение для любого, кто может найти это в будущем.

Я попытался отправить запрос несколькими способами:

  • Объекты сценария AJAX, созданные при добавлении службы WCF в качестве ServiceReference в ScriptManager. Итак, если класс службы WCF - это ProgressService с методом GetProgress, я создал новый объект ProgressService в JavaScript и назвал progressService.GetProgress ().
  • Запрос XmlHttpRequest.
  • Запрос jQuery $ .getJson ().
  • Запрос Sys.Net.WebRequest.
  • Запрос Sys.Net.WebServiceProxy.

Оказывается, что даже если клиентский запрос отправлен, т.е. не буферизован ASP.NET ScriptManager, служба WCF не будет отвечать, если она является частью того же веб-сайта в IIS.

Поэтому вместо того, чтобы создавать совершенно отдельный проект WCF и совершенно отдельный веб-сайт IIS, я переключился на традиционную веб-службу ASP.NET (SOAP) (.asmx).

Мне удалось сохранить часть службы .asmx того же проекта в Visual Studio и веб-сайт в IIS. Запрос отправляется во время обратной передачи, а служба отвечает во время обратной передачи.

После добавления его в качестве ServiceReference под ScriptManager я также смог по существу использовать те же самые объекты сценариев, создав новый ProgressWebService (), затем вызвав progressWebService.GetProgress (). Обработчик обратного вызова, переданный в GetProgress (), затем обрабатывает ответ и обновляет пользовательский интерфейс.

Веб-сервис:

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);
        }
    }   
}
0 голосов
/ 22 мая 2010

Ajax-сантехника может ставить в очередь ваши запросы.

Попробуйте сделать статусный вызов вручную с помощью XHR или jQuery.Вы можете обнаружить, что это решает проблему.

Но ... имейте в виду, что существует конечное число одновременных запросов, которые могут выполняться за один раз, и что после достижения предела начинает происходить блокировка.1005 *

Это ограничение зависит от браузера / версии.

...