Управление состоянием ASP.NET
Упражнение 11
В качестве примера построим две страницы, в которых подсчитывается общее число посещений сайта с момента запуска приложения.
-
Добавьте к приложению файл Global.asax и заполните его так
<%@ Application Language="C#" %> <script runat="server"> void Application_Start(object sender, EventArgs e) { this.Application["clicks"] = (int)0; } </script>
-
Создайте страницу без отделенного кода с именем ApplicationState1.aspx, назначьте ее стартовой и заполните так
<%@ Page Language="C#" %> <script runat="server"> protected void Page_Load(object sender, EventArgs e) { // Читаем без проверки - инициализирована в global.asax int visit = (int)this.Application["clicks"] + 1; // Обновляем состояние приложения с блокировкой this.Application.Lock(); this.Application["clicks"] = visit; this.Application.UnLock(); // Создаем текстовую метку Label label = new Label(); form1.Controls.Add(label); label.Text = "Сегодня вы посетили нас в " + visit.ToString() + " раз!<br />"; // Создаем гиперссылку HyperLink link = new HyperLink(); form1.Controls.Add(link); link.NavigateUrl = "~/ApplicationState2.aspx"; link.Text = "На другую страницу..."; } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> </form> </body> </html>
- Создайте копию для страницы ApplicationState1.aspx с именем ApplicationState2.aspx и поменяйте в ней гиперссылку на ApplicationState1.aspx, чтобы страницы ссылались друг на друга
- Запустите само приложение несколько раз и пощелкайте по ссылкам чтобы убедиться, что счетчик количества визитов увеличивается
- В панели задач нижней части экрана щелкните правой кнопкой мыши на пиктограмме тестового сервера и остановите его командой Stop
- Вновь запустите страницу ApplicationState1.aspx, чтобы убедиться, что счетчик сбросился в начальное значение
Хранение данных в статических переменных приложения
Для увеличения масштабируемости приложение загружается в память сервера в виде множества копий (пула). Каждый экземпляр класса приложения имеет свой набор данных и только объект-тип хранит все методы и статические данные в единственном экземпляре. Если в файле Global.asax объявить общедоступные статические поля, то в них можно хранить данные, которые будут видимы через имя класса приложения из любой страницы сайта. Но при этом нужно явно определить в директиве @ Application файла Global.asax параметр ClassName, в котором указывается имя класса приложения для последующего обращения из кода страниц.
При изменении в страницах глобальных данных класса приложения механизм автоматической блокировки отсутствует. Поэтому нужно пользоваться встроенным в язык C# оператором lock, чтобы на время позволить воздействовать на переменную только со стороны одного потока. Такой подход удобен тем, что мы можем обернуть сохраняемые данные в статические свойства и назначить для них контролирующий код, прежде чем сохранять их в памяти сервера. Мы можем объявлять статические переменные для хранения объектов любого типа и для доступа к ним явных преобразований типа не потребуется.
Упражнение 12
Приведем пример, в котором количество визитов всех пользователей, а также список файлов текущего физического корневого каталога сервера будем хранить в статических переменных класса приложения.
-
Отредактируйте код файла Global.asax следующим образом
<%@ Application Language="C#" ClassName="MyAppClass" %> <script runat="server"> // Список файлов private static string[] fileList;// Базовое поле // Общедоступное статическое свойство для обертки базового поля public static string[] FileList { get { // Заполнить список только при первом запросе if (fileList == null) { fileList = System.IO.Directory.GetFiles( HttpContext.Current.Request.PhysicalApplicationPath); } return fileList; } } // Число посещений private static int visit = 1;// Базовое поле // Общедоступное статическое свойство для обертки базового поля public static int Visit { get { return visit; } set { lock (typeof(int))// Контейнер обеспечения синхронизации потоков { visit = value; } } } </script>
- Добавьте к приложению страницу без отделенного кода с именем AppClassStaticMembers.aspx и назначьте ее стартовой
-
Заполните страницу AppClassStaticMembers.aspx следующим кодом
<%@ Page Language="C#" EnableViewState="false" %> <script runat="server"> protected void Page_Load(object sender, EventArgs e) { // Заголовок Label title = new Label(); form1.Controls.Add(title); title.Text = "<h2 style='text-align: center; color: Red'>" + "Использование статических" + " полей класса приложения</h2>"; // Число посещений Label visit = new Label(); form1.Controls.Add(visit); int clicks = MyAppClass.Visit; visit.Text = "<b>Количество посещений: " + clicks.ToString() + "</b>"; MyAppClass.Visit = ++clicks; // Горизонтальная черта form1.Controls.Add(new HtmlGenericControl("hr")); // Извлекаем и готовим список файлов // текущего каталога приложения StringBuilder builder = new StringBuilder(); builder.Append("<ol>"); // Счетчик для сокращения листинга int count = 0; const int maxCount = 5; foreach (string file in MyAppClass.FileList) { if (++count > maxCount) break; string str; //int pos = file.LastIndexOf(@"\"); //str = file.Substring(pos + 1); // То же самое str = System.IO.Path.GetFileName(file); builder.Append("<li>" + str + "</li>"); } builder.Append("</ol>"); // Готовим текстовую метку для отображения списка Label fileInfo = new Label(); form1.Controls.Add(fileInfo); fileInfo.Text = builder.ToString(); // Горизонтальная черта form1.Controls.Add(new HtmlGenericControl("hr")); // Создаем дескриптор центрирования кнопки HtmlGenericControl center = new HtmlGenericControl("center"); form1.Controls.Add(center); // Кнопка Submit добавляется в контейнер центрирования Button button = new Button(); button.Text = "Отправить"; center.Controls.Add(button); } // Переопределяем обработчик рендеринга protected override void Render(HtmlTextWriter writer) { // Сгенерировать основное base.Render(writer); // Добавить в конец еще HTML-вывод // Настроить атрибуты будущего дескриптора writer.AddAttribute(HtmlTextWriterAttribute.Align, "Center"); writer.AddStyleAttribute(HtmlTextWriterStyle.Color, "Blue"); // Создать дескриптор writer.RenderBeginTag(HtmlTextWriterTag.H2); // Писать внутрь дескриптора writer.Write("Щелкайте на кнопке и наблюдайте счетчик посещений"); // Закрыть дескриптор writer.RenderEndTag(); } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> </form> </body> </html>
- Запустите страницу и убедитесь, что все работает и объявленные в классе приложения статические члены сохраняют свои значения даже при отключении клиента от сервера до тех пор, пока сервер остается запущенным
- В панели задач нижней части экрана щелкните правой кнопкой мыши на пиктограмме тестового сервера и остановите его командой Stop
- Вновь запустите страницу AppClassStaticMembers.aspx, чтобы убедиться, что память сервера очистилась и счетчик сбросился в начальное значение
А сейчас мы убедимся, что при модификации любого файла приложения, включая web.config, оно автоматически перекомпилируется при первом запросе и загружается в новый домен.
- Не нарушая кода, модифицируйте любой исходный файл приложения, например, введите пробел после точки с запятой в конце любой строки (или добавьте пустую строку) и сохраните изменения
- Запустите приложение и убедитесь, что счетчик сбросился за счет загрузки приложения в новый домен
Интерфейс клиента для данного примера будет таким
Рассмотренные в данной теме способы хранения данных относятся, в основном, к внутрипроцессным, так как сохраняются в оперативной памяти сервера или передаются по каналам связи на компьютер клиента для временного хранения. Другие методы хранения информации в службах сервера, включая базы данных и файлы, являются внепроцессными, поскольку не зависят от среды исполнения ASP.NET.