Опубликован: 07.05.2010 | Уровень: для всех | Доступ: платный
Лекция 10:

Управление состоянием 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.


Илья Онучин
Илья Онучин
Россия
Igor Chelyadinski
Igor Chelyadinski
Беларусь, Минск, №54, 2013