Хочу таймер и счётчик загруженных строк на Web-форме

от автора


… отчет формировался долго. Песочные часы и синяя полоска клонили в сон. Вначале он хотел обезьянку, которая лезет по лестнице, потом градусник, но в итоге остался таймер и счетчик. Глупая улыбка и добрый, сочувствующий, взгляд делают чудеса.

В процессе разработки форм для отчетности, пользователь захотел видеть процесс загрузки данных из базы. Он хотел, чтобы после нажатия кнопки включался секундомер, а по мере получения строк, их количество отображалось на форме.Реализовать это надо было в рамках существующего проекта на ASP.NET.

Решая эту задачу, я столкнулся с проблемой выбора способа обмена данными с сервером, так как ему надо было передать данные и получить какой-то ответ, не перегружая страницу. Помимо кардинального метода – переписать всё на MVC, есть и другие способы. Большинство из них было описано в статье Как выполнить callback со стороны клиента на сервер в ASP.NET. Так же можно пользоваться Callback контролами от DevExpress или ему подобных коммерческих продуктах.
В описываемом случае я воспользовался методом, о котором узнал, читая Дино Эспозито. Речь идёт об HTTPHandler. Его преимущество – это минимум кода, который выполняет сервер по сравнению с другими способами, отсюда высокое быстродействие, чего очень не хватает ASP.NET приложениям. На стороне клиента я использовал JQuery и функцию setTimeout.

Код на стороне сервера выглядит так:

Файл TestForm.aspx.cs:
using System;  namespace HTTPHandler_Test {     public partial class TestForm : System.Web.UI.Page     {         protected void Page_Load(object sender, EventArgs e)         {          }     } } 
HTTPHandler для теста — HTTPHandlerTest.ashx.cs :

using System.Web; using System.Web.SessionState;  namespace HTTPHandler_Test.HTTPHandler {     /// <summary>     /// Сводное описание для HTTPHandlerTest     /// </summary>     public class HTTPHandlerTest : IHttpHandler, IRequiresSessionState     {         public void ProcessRequest(HttpContext context)         {             var counter = context.Request.QueryString["counter"];             int iCount;             if (int.TryParse(counter, out iCount))             {                 counter = (++iCount).ToString();             }             context.Response.Write(counter);         }                   public bool IsReusable         {             get             {                 return true;             }         }     } } 

На стороне клинета так же надо добавить пару строк:

Файл TestForm.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TestForm.aspx.cs" Inherits="HTTPHandler_Test.TestForm" %>  <!DOCTYPE html>  <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>     <title></title>     <script src="Script/jquery-1.10.2.min.js"></script>     <script src="Script/askHTTPHandler.js"></script>      </head> <body>     <form id="form1" runat="server">     <div>         <label id="lblStatus" style="display:block">Таймер: </label>         <label id="lblCounter" style="display:block">Счетчик: </label>         <input type="button" id="btnStartStop" value="Старт" onclick="AskHttpHandler()"/>     </div>     </form> </body> </html> 
Файл askHTTPHandler.js:
var start = false;  function AskHttpHandler() {     var dateStart = new Date();     var counter = 0;     start = !start;     if (start) {         $('#btnStartStop').val("Стоп");         Ask();     }     else $('#btnStartStop').val("Старт");      function Ask() {         var difInSeconds = Math.floor(((new Date()).getTime() - dateStart.getTime()) / 1000);         var hours = Math.floor(difInSeconds / 3600);         var minutes = Math.floor((difInSeconds - (hours * 3600)) / 60);         var seconds = difInSeconds - (hours * 3600) - (minutes * 60);         if (hours < 10) hours = "0" + hours;         if (minutes < 10) minutes = "0" + minutes;         if (seconds < 10) seconds = "0" + seconds;         $('#lblStatus').text("Таймер: " + hours + ":" + minutes + ":" + seconds);          var $ajaxQ = $.ajax({             type: "GET",             async: false,             url: "/HTTPHandler/HTTPHandlerTest.ashx",             data: "counter=" + counter,             success: onSuccessAsk,             error: onErrorAsk         });          var noop = function () { };         if ($ajaxQ != null) {             $ajaxQ.onreadystatechange = $ajaxQ.abort = noop;             $ajaxQ = null;         }          function onSuccessAsk(result) {             counter = parseInt(result);             $('#lblCounter').text("Счетчик: " + result);             if (start) setTimeout(Ask, 1000);         }          function onErrorAsk(result) {             alert("error " + result.responseText);         }     }; } 

В процессе разработки обнаружилась досадная утечка памяти на стороне клиента. Решил её с помощью кода ниже:

var noop = function () { };         if ($ajaxQ != null) {             $ajaxQ.onreadystatechange = $ajaxQ.abort = noop;             $ajaxQ = null;         } 

Предварительно полазив по интернету и наткнувшись на это ссылку: Memory Growing to Heaven

Заключение

В процессе работы с ASP.NET все больше и больше приходиться смешаться в сторону толстого клиента и использовать сервер как источник данных. HTTPHandler неплохо справляется с этой задачей, позволяя сохранить специфический для ASP.NET функционал и обойти его недостатки.

P.S.

Данное решение, помимо информирования пользователя, так же помогло отловить баги связанные с провайдером к БД Oracle. Время от времени, при получении данных от сервера, скорость чтения строк начинала очень быстро падать. Исправить это получилось установкой Min Pool Size и Max Pool Size в значение 23.

ссылка на оригинал статьи http://habrahabr.ru/post/253875/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *