asp.net AJAX лучшие практики для клиентов, вызывающих медленный асинхронный процесс на сервере - PullRequest
4 голосов
/ 22 ноября 2011

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

Я реализовал сервис как WCF.Я генерирую асинхронные методы, когда добавляю ссылку на службу в мой веб-клиент.

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

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

Я делаю этомини-проект как учебное упражнение, поэтому я собираюсь сделать то же самое с MVC3, чтобы узнать различия.

Фрагмент кода (без отдельного потока, вызывающий замедление рендеринга экрана во время обратного вызова):

//get list of connections from session
ConnectionList myConns = Session[SESSION_ID] as ConnectionList;
//pass into async service call
GetAllStatusAsync(myConns);


protected void GetAllStatusAsync(ConnectionList myConns)
{

Service1Client myClient = new WcfConnectionServiceRef.Service1Client();
myClient.AsyncWorkCompleted += new EventHandler<AsyncWorkCompletedEventArgs>(myClient_AsyncWorkCompleted);

foreach (ConnectionDetail conn in myConns.ConnectionDetail)
  {
  //this call isnt blocking, conn wont be updated until later in the callback
  myClient.AsyncWorkAsync(conn);
  }
}

//callback method from async task
void myClient_AsyncWorkCompleted(object sender, AsyncWorkCompletedEventArgs e)
{

ConnectionDetail connResult = e.Result;

//get list of connections from session
ConnectionList myConns = Session[SESSION_ID] as ConnectionList;

//update our local store
UpdateConnectionStore(connResult, myConns);

//rebind grid
BindConnectionDetailsToGrid(myConns);

}

Вопрос в том, можно ли сделать это лучше в asp.net / AJAX?(Чтобы избежать проблем с блокировкой рендеринга и частичного обновления сетки при получении результатов) Я не хочу использовать отдельный клиентский поток, такой как следующий фрагмент:

 // Perform processing of files async in another thread so rendering is not slowed down 
 // this is a fire and forget approach so i will never get results back unless i poll for them in timer from the main thread
ThreadPool.QueueUserWorkItem(delegate
  {
  //get list of connections from session
  ConnectionList myConns = Session[SESSION_ID] as ConnectionList;
  //pass into async service call
  GetAllStatusAsync(myConns);
  });

ОБНОВЛЕНИЕ:

Добавление разметки страницы в соответствии с запросом:

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ASForm.aspx.cs" Inherits="Web_Asp_FBMonitor.ASForm" Async="true" EnableSessionState="True" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>
        ASP.NET Connection Test (Client in ASYNC, Server in ASYNC)
    </h2>

    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>


    <p>

        <%--This update panel shows the time, updated every second--%>    
        &nbsp;<asp:UpdatePanel ID="UpdatePanel2" runat="server">
        <ContentTemplate>

            <h3> <asp:Label ID="LabelTime" runat="server" Text=""></asp:Label>  </h3>
            <asp:Timer ID="Timer1" runat="server" Interval="1000" ontick="Timer1_Tick">  </asp:Timer>

            </ContentTemplate>
    </asp:UpdatePanel>
    </p>

    <p>

        <%--This update panel shows our results grid--%>
        &nbsp;<asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>

            <asp:GridView ID="GridView1" runat="server">
            </asp:GridView>

            <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Client Sync Page</asp:HyperLink>

            <br />

            <asp:Button ID="ButtonUpdate" runat="server" Text="Update" 
                onclick="ButtonUpdate_Click" />
        </ContentTemplate>
        </asp:UpdatePanel>




    </p>



</asp:Content>

ОБНОВЛЕНИЕ 2:

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

Ответы [ 4 ]

2 голосов
/ 30 ноября 2011

Вы видите то, что вы называете «блокировками экрана» из-за ASP UpdatePanel s.

Веб-формы ASP.NET - это попытка заставить веб работать как формы Windows.Восхитительный?Зависит от того, кого вы спрашиваете.

Когда вы используете UpdatePanel, ASP.NET сохраняет серверные элементы управления в ViewState, вносит любые необходимые изменения в этот ViewState (в вашем случае это обновлениестраница, основанная на коде в ваших Timer_Tick и ButtonUpdate_Click функциях).Затем он обновляет страницу новыми данными, вызывая описанные вами блокировки экрана.

Чтобы обойти это, вам придется использовать настоящий AJAX.Многие люди делают это с помощью функций jQuery AJAX и одной из следующих опций:

  • ASP.NET WebMethods
  • службы WCF
  • Отдельные страницы ASP.NET

Здесь, на SO, довольно много вопросов о подключении ASP.NET WebMethods через jQuery, и некоторые о загрузке страниц ASP.NET, но не так много вопросов об AJAX и WCF.

Если вы решите использовать AJAX и jQuery, ваша получившаяся страница будет выглядеть примерно так:

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ASForm.aspx.cs" Inherits="Web_Asp_FBMonitor.ASForm" Async="true" EnableSessionState="True" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
<!-- include jQuery library here -->
<script language="javascript" type="text/javascript">
    $(document).ready(function () {
        UpdateGrid();

        // Separate AJAX call to another page, WCF service, or ASP.NET WebMethod for the Timer results
    });

    function UpdateGrid() {
        $.ajax({ url: "GridViewResults.aspx",
            done: function (result) {
                $("#gridResults").html(result.d);
            }
        });
    }
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>
        ASP.NET Connection Test (Client in ASYNC, Server in ASYNC)
    </h2>

    <p>
        <h3> <span id="timer"></span> </h3>
    </p>

    <p id="gridResults">
    </p>
    <button id="ButtonUpdate" onclick="UpdateGrid();">Update</button>
</asp:Content>

Тогда на отдельной странице (я произвольно назвал ее GridViewResults.aspx выше) вы бы получили:

<asp:GridView ID="GridView1" runat="server"></asp:GridView>
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Client Sync Page</asp:HyperLink>
<br />
1 голос
/ 02 декабря 2011

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

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

Если вы хотите получать обновления в сетке по мере поступления результатов насервер, есть несколько потенциально более чистых альтернатив:

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

  2. Переключитесь на использование WebSockets.Это двунаправленное соединение для передачи данных.Сервер может периодически запрашивать информацию, используя асинхронные запросы, а затем отправлять результаты клиенту по мере их поступления.Затем клиент будет использовать скрипт, как в # 1 выше, для обновления сетки.

  3. Использовать длинный опрос.Иметь фоновый поток, который периодически опрашивает информацию и поддерживает структуры данных с текущим состоянием и либо порядковым номером, либо отметкой времени.Когда клиент делает Ajax-запрос на обновление, он передает последнюю полученную временную метку или порядковый номер.Затем серверный код проверяет, есть ли что-нибудь новое в структурах данных фонового потока.Если это так, он обновляет сетку и возвращает.Если нет, он переходит в спящий режим (ожидает общей блокировки) до тех пор, пока фоновый поток не получит обновление, после чего он сообщит о блокировке, в результате чего поток запроса проснется, обновит страницу с последними данными и вернется.

Вот пример кода, использующего jQuery для асинхронного вызова Ajax:

$.get('myinfo.ashx', function(data) {
  $('.result').html(data);
})

Подробнее: http://api.jquery.com/jQuery.get/

1 голос
/ 23 ноября 2011

Возможно, вы захотите взглянуть на «асинхронные» страницы в ASP.net.Это позволит вам сделать один обратный вызов AJAX и заставить сервер выполнять все опросы асинхронно на стороне сервера.Затем вы можете перепривязать сетку, когда все задачи были асинхронно возвращены и у вас есть полный набор данных.

Ссылка на статью, объясняющую это:

http://msdn.microsoft.com/en-us/magazine/cc163725.aspx


ОК, основываясь на отзывах из комментариев, вы хотите обновить сетку при каждом обновлении.Я не уверен, как вы отбрасываете свои AJAX-запросы из браузера, но вы можете захотеть взглянуть на их асинхронное отключение от клиента (что-то вроде jQuery может быть полезно здесь), а затем снова использовать скрипт для перерисовки конкретной строкисетки, которая вам нужна, когда вы получите результат.

0 голосов
/ 30 ноября 2011

Рассматривали ли вы использование длинного опроса / постоянного соединения.

В настоящее время имеется отличная инфраструктура для упрощения реализации в ASP.net под названием SignalR .

Вот пара статей, с которых можно начать:

http://www.hanselman.com/blog/AsynchronousScalableWebApplicationsWithRealtimePersistentLongrunningConnectionsWithSignalR.aspx

http://www.amazedsaint.com/2011/11/introduction-ksigdo-knockout-signalr-to.html

Для описываемой вами проблемы она звучит так, как будто это может бытьхорошо подходит, поскольку позволяет обновлять экран при отправке данных абоненту.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...