Разработка на ASP.NET. Жизненный цикл страницы, пользовательский интерфейс
6.2.3. Состояние просмотра ViewState
Веб-элементы управления ASP.NET используют состояние просмотра по умолчанию. Оно позволяет им сохранять их свойства между отправками данных (postback). С помощью свойства ViewState можно добавить в коллекцию состояния просмотра свои собственные данные. В частности, можно здесь хранить простые типы данных и свои собственные специальные объекты.
Как и в основе большинства опций для управления состоянием в ASP.NET, в основе состояния просмотра лежит коллекция типа словаря, где каждый элемент индексируется с помощью уникального строкового имени. Например:
ViewState["CurrentPage"] = 3;
Этот код помещает в коллекцию ViewState значение 3 (или, скорее, целое число, которое содержит значение 3) и присваивает ему описательное имя CurrentPage. Если в коллекции ViewState на текущий момент нет элемента с именем CurrentPage, в нее будет автоматически добавлен такой новый элемент. Если элемент с таким именем уже существует, он будет заменен.
При извлечении значения следует использовать имя ключа. Помимо этого, также следует привести извлекаемое значение к соответствующему типу данных. Этот дополнительный шаг является обязательным, потому что коллекция ViewState сохраняет (хранит) все элементы в виде объектов обобщенного типа, что позволяет ей иметь дело со многими различными типами данных.
Ниже показан код, который извлекает значение CurrentPage и преобразовывает его в целое число:
int counter; if (ViewState["CurrentPage"] != null) { counter = (int)ViewState["CurrentPage"]; }
При попытке отыскать значение, которого нет в коллекции, будет выдано исключение NullReferenceException. Чтобы исключить возможность возникновения такой ситуации, прежде чем пытаться извлечь и привести к определенному типу данные, которых может и не быть, следует выполнить проверку на наличие значения null.
6.2.3.1. Сохранение объектов в состоянии просмотра
В состоянии просмотра можно сохранять свои собственные объекты, причем так же легко как числовые и строковые типы. Однако чтобы элемент был сохранен в состоянии просмотра, ASP.NET должен быть способен преобразовать его в поток байтов для того, чтобы он мог быть добавлен в скрытое поле для ввода данных на странице. Этот процесс называется сериализацией. Если объекты не поддаются сериализации (а по умолчанию это именно так), при попытке поместить их в состояние просмотра, появится сообщение об ошибке.
Чтобы сделать объекты пригодными для сериализации, следует просто добавить перед объявлением соответствующего класса атрибут Serializable. Например, ниже показан чрезвычайно простой класс BookPage:
[Serializable] public class BookPage { public int PageNamber; public string Text; public BookPage(int pageNamber, string text) { PageNamber = pageNamber; Text = text; } }
Поскольку класс BookPage помечен как пригодный для сериализации/поддающийся сериализации, он может быть сохранен в состоянии просмотра:
BookPage page = new BookPage (3, "Понедельник начинается в субботу"); ViewState["CurrentPage"] = page;
Не забывайте о том, что когда используются специальные объекты, при извлечении из состояния просмотра данные обязательно должны приводиться к какому-нибудь типу:
BookPage cust = (BookPage)ViewState["CurrentPage"];
Чтобы класс поддавался сериализации, он должен отвечать следующим требованиям:
- он должен иметь атрибут Serializable ;
- все порождаемые от него классы тоже должны иметь атрибут Serializable ;
- все индивидуальные переменные этого класса должны представлять собой поддающиеся сериализации типы данных, а все не поддающиеся сериализации типы данных должны сопровождаться атрибутом NonSerialized (во время процесса сериализации они будут просто игнорироваться).
6.2.3.2. Оценивание преимуществ использования состояния просмотра
Применение состояния просмотра является идеальным вариантом, потому что не предполагает использования никаких ресурсов памяти сервера и появления случайных ограничений (типа таймаута). Но есть и некоторые причины, когда состояние просмотра лучше не применять:
- Когда необходимо сохранить критически важные данные, возможность изменения пользователем которых должна быть полностью исключена (например, сообразительный пользователь мог бы изменить данные состояния просмотра в запросе на обратную отправку данных). В таком случае, лучше использовать состояние сеанса.
- Когда необходимо сохранить информацию, которая будет использоваться несколькими страницами. В таком случае больше подойдет состояние сеанса, cookie-наборы или строка запроса.
- Когда необходимо сохранить чрезвычайно большой объем информации и не хочется, чтобы это повлияло на скорость передачи данных страницы. В таком случае может оказаться лучше использовать базу данных или состояние сеанса.
Чтобы повысить скорость передачи данных на странице, состояние просмотра, если оно не используется, лучше отключить. Хотя отключить состояние просмотра можно как на уровне приложения, так и на уровне страницы, наиболее правильный вариант – отключить его на уровне элемента управления. Элемент управления не нуждается в состоянии просмотра в трех следующих случаях:
- Когда этот элемент управления никогда не изменяется. Например, кнопка со статическим текстом не нуждается в состоянии просмотра.
- Когда этот элемент управления заново заполняется данными после каждой отправки данных. Например, элемент управления типа надписи ( Label ), отображающий текущее время, значение для которого устанавливается в обработчике событий Page.Load, не нуждается в состоянии просмотра.
- Когда этот элемент управления представляет собой элемент управления для ввода данных и изменяется только после того, как пользователь выполнит соответствующие действия. После каждой отправки данных ASP.NET будет заполнять такие элементы управления данными, используя введенные в форме значения. Это означает, что текст в текстовом поле или выбранный элемент в поле со списком не будут утрачиваться, даже если состояние просмотра не используется.
Чтобы отключить состояние просмотра для какого-нибудь одного элемента управления, необходимо установить для свойства EnableViewState этого элемента управления значение false. Чтобы отключить состояние просмотра для всей страницы и всех отображаемых на ней элементов управления, необходимо установить значение false для свойства EnableViewState этой страницы или используйте в директиве Page атрибут EnableViewState, как показано ниже:
<%@ Page Language="c#" EnableViewState="false" ... %>
Даже если состояние просмотра будет отключено для всей страницы, все равно будет виден скрытый дескриптор состояния просмотра с небольшим количеством информации. Это связано с тем, что ASP.NET всегда сохраняет как минимум иерархию элементов управления для страницы, даже если состояние просмотра было отключено. Так что удалить этот последний небольшой фрагмент данных никак не получится.
6.2.4. Строка запроса
Одним из самых серьезных ограничений состояния просмотра является то, что оно работает только на одной странице. Если пользователь переходит на другую страницу, сохраненная в нем информация утрачивается. Существует несколько способов решить эту проблему; то, какой из них будет наиболее подходящим, зависит от имеющихся требований.
Наиболее распространенный подход – передавать информацию с помощью строки запроса в URL-адресе. Именно этот подход часто применяется в поисковых службах. Например, при выполнении поиска на Веб-сайте Google, пользователь будет перенаправлен на новый URL-адрес, включающий указанные им параметры поиска:
http://www.google.ru/search?ie=UTF-8&hl=ru&q=evolution&redir_esc=y&ei=iRKRS5ndN5-inQOH96DTBg
Строка запроса – это та часть URL-адреса, которая находится после вопросительного знака.
Преимуществом строки запроса является то, что она проста и не возлагает никакой нагрузки на сервер. В отличие от межстраничной пересылки данных, строка запроса может запросто переносить одну и ту же информацию со страницы на страницу. Однако она имеет несколько ограничений:
- она может передавать информацию только в виде простых строк, которые должны содержать допустимые для использования в URL-адресе символы;
- передаваемая с помощью строки запроса информация визуально доступна для пользователя и любого другого работающего в Интернете человека;
- инициативный пользователь может решить изменить строку запроса и предоставить новые значения, которые программа никак не ожидает получить и от которых она не имеет защиты;
- многие браузеры имеют ограничения касательно длины URL-адреса (которая, как правило, не должна превышать 1-2 КБ), поэтому в строку запроса нельзя поместить большое количество информации, и все равно придется выполнять проверку на совместимость с большинством браузеров.
И все-таки добавление информации в строку запроса является полезной технологией. Она особенно подходит для приложений баз данных, в которых пользователю отображается список элементов, соответствующих записям в базе данных (например, это может быть список продуктов): пользователь выбирает элемент, после чего он перенаправляется на другую страницу, содержащую детальную информацию о том элементе, который был им выбран. Один из наиболее простых способов реализовать такую схему – это заставить первую страницу отправлять идентификатор элемента второй странице. Вторая страница затем будет отыскивать этот элемент в базе данных и отображать детальную информацию о нем. Эта технология очень часть применяется на сайтах электронной коммерции, таких как Amazon.com.
6.2.4.1. Использование строки запроса
Чтобы сохранить информацию в строке запроса, ее придется сначала самостоятельно поместить туда. Как правило, это означает, что придется использовать специальный элемент управления HyperLink или оператор Response.Redirect(), подобный тому, который показан ниже:
// Переходим на страницу products/default.aspx. int categoryID = 10; Response.Redirect("products/default.aspx?category=" + recordID.ToString());
Отправлять можно и несколько параметров, только тогда их следует разделять с помощью амперсанда ( & ), как показано ниже:
Response.Redirect("products/default.aspx?category=" + categoryID.ToString()+"&subcategory="+subcategoryId.ToString());
Получающей странице легче работать со строкой запроса. Она может получать значения из коллекции типа словаря QueryString, предоставляемой встроенным объектом Request, как показано ниже:
string ID = Request.QueryString["category"];
Стоит обратить внимание на то, что информация всегда извлекается в виде строки, которая затем может быть преобразована в какой-нибудь другой простой тип данных. Значения в коллекции QueryString индексируются по имени переменной.
6.2.5. Cookie
Специальные cookie-наборы – это еще один способ сохранить информацию для последующего использования. Cookie-наборы представляют собой небольшие файлы, которые создаются на жестком диске клиента (или, если они являются временными, в памяти Веб-браузера). Одним из преимуществ cookie-наборов является то, что они работают "прозрачно" и пользователь даже не знает о том, что эта информация должна быть сохранена. Они также могут запросто использоваться любой страницей в приложении и даже сохраняться между посещениями, что подразумевает действительно долгосрочное хранение. Они имеет те же недостатки, что и строки запросов. А именно: они могут хранить только простую строковую информацию, и пользователь запросто может получить к ним доступ и просмотреть их, отыскав и открыв соответствующий файл. Эти факторы делают их неподходящим вариантом, когда требуется сохранить сложную или секретную информацию или просто большие объемы данных.
Некоторые пользователи отключают функцию, отвечающую за cookie-наборы, в своих браузерах, что приводит к появлению проблем при работе с Веб-приложениями, которые требуют их. В целом, cookie-наборы можно назвать распространенным явлением, потому что так много сайтов используют их. Однако они могут ограничивать потенциальную аудиторию и никак не подходят для браузеров, встроенных в мобильные устройства.
Прежде чем использовать cookie-наборы, следует импортировать пространство имен System.NET, которое позволяет работать с соответствующими типами, как показано ниже:
using System.Net;
В использовании cookie-наборов в принципе нет ничего особого сложного. Коллекцию Cookies предоставляют два объекта: объект Request и объект Response (оба из которых предоставляются через свойства объекта Page ). Главное запомнить следующее: извлекать cookie-наборы следует из объекта Request, а устанавливать их следует с помощью объекта Response.
Чтобы установить cookie-набор, необходимо создать новый объект System.Net.HttpCookie. Затем, необходимо заполнить его строковой информацией и присоединить его к текущему Веб-ответу, как показано ниже:
// Создаем объект cookie HttpCookie cookie = new HttpCookie("Language"); // Устанавливаем в нем значение cookie["LanguagePref"] = "English"; // Добавляем его в текущий Веб-ответ Response.Cookies.Add(cookie);
Cookie-набор, добавленный таким образом, будет сохраняться до тех пор, пока пользователь не закроет окно браузера, и будет отправляться вместе с каждым запросом. Чтобы создать cookie-набор с более длинным сроком хранения, необходимо установить дату истечения срока, как показано ниже:
// Этот cookie будет оставаться действительным в течение одного года. cookie.Expires = DateTime.Now.AddYears(1);
Извлекаются cookie по имени с помощью коллекции Request.Cookies, как показано ниже:
HttpCookie cookie = Request.Cookies["Language"]; // Проверяем, был ли найден cookie с таким именем. // Это хорошая мера предосторожности, потому что пользователь может отключить поддержку cookie's, //а в таком случае cookie не будет существовать. string language; if (cookie != null) { language = cookie["LanguagePref"]; }
Единственный способ удалить cookie – это заменить его на cookie-набор, дата истечения срока хранения которого уже прошла. Следующий фрагмент кода демонстрирует, как это можно сделать:
HttpCookie cookie = new HttpCookie("LanguagePref"); cookie.Expires = DateTime.Now.AddDays(-1); Response.Cookies.Add(cookie);