Опубликован: 07.05.2010 | Доступ: свободный | Студентов: 1678 / 62 | Оценка: 4.56 / 4.06 | Длительность: 34:11:00
Лекция 10:

Управление состоянием ASP.NET

Сохранение данных в состоянии сеанса

Любой пользователь при первом обращении к приложению получает свой сеанс, который выражается в выделении некоторого объема памяти на стороне сервера для хранения данных любого типа. Доступ к этим совместным данным осуществляется любой страницей через присоединенный к ней объект-словарь Session. За счет этого можно сохранять информацию на одной странице, а получать к ней доступ с другой страницы.

При поступлении первого клиентского запроса на объект страницы ASP.NET генерирует идентификатор сеанса размером 42 байта. Этот идентификатор сеанса включается в отклик сервера и является уникальным в пределах приложения. Если ни одна из страниц приложения, к которым обращается пользователь, не записывает в объект Session данные, то броузер не возвращает его в запросе серверу. Вместо этого сервер при каждом запросе генерирует новый идентификатор сеанса и включает его в отклик. Стоит только странице записать какие-нибудь данные в объект Session, идентификатор сеанса сразу запоминается броузером и начинается обратный процесс. Теперь уже броузер с каждым запросом начинает посылаться на сервер сохраненный идентификатор сеанса, даже если в дальнейшем мы полностью очистим объект Session.

Когда клиент присылает идентификатор сеанса, сервер по нему автоматически извлекает из места хранения данные сеанса и заполняет ими объект Session созданной страницы. После чего данные сеанса становятся доступными в коде этой страницы.

Идентификатор сеанса может пересылаться серверу с запросом двумя способами:

  1. С помощью cookie-набора с именем ASP.NET_SessionId
  2. С помощью включения в URL для клиентов, не поддерживающих cookie-наборы (использование измененных URL - адресов)

Идентификатор сеанса можно получить как значение Response.Cookies["ASP.NET_SessionID"] - при формировании первого отклика, или Session.SessionID - всегда. Но конкретное его значение нас, как правило, не интересует, поскольку процесс распознавания выполняется автоматически.

Данные сеанса могут сохраняться ASP.NET в одном из трех мест на сервере, определяемых свойством Mode объекта Session:

  1. В оперативной памяти сервера внутри процесса - текущего домена приложения (режим InProc - установлен по умолчанию)
  2. В оперативной памяти сервера вне процесса в специальной службе Windows под названием ASP.NET State Service (режим StateServer )
  3. Вне процесса на жестком диске в базе данных SQL Server (режим SQLServer )

Состояние сеанса утрачивается в следующих случаях:

  1. Пользователь закрывает и вновь запускает броузер
  2. Пользователь получает доступ к приложению через другой броузер
  3. Из-за простоя сеанса больше определенного времени по причине отсутствия запросов от клиента (по умолчанию 20 минут)
  4. Программист завершает сеанс методом Session.Abandon()
  5. Если сеанс создается внутри текущего домена, то при переходе приложения в новый домен сеанс будет потерян

Сеанс является экземпляром класса System.Web.SessionState.HttpSessionState и может частично управляться из кода. В таблице приведены некоторые свойства и методы класса HttpSessionState

Некоторые свойства и методы класса HttpSessionState
Член Описание
Count Количество элементов в коллекции текущего сеанса. Только для чтения
IsCookieless Булев флаг, показывающий способ хранения идентификатора сеанса: в куках или в модифицированном URL. Только для чтения
IsNewSession Булево значение, которое указывает, был ли сеанс создан с текущим запросом. Если коллекция пуста или сеанс создан ранее, то возвращается false. Только для чтения
Mode Свойство (только для чтения) содержит одно из значений перечисления SessionStateMode, указывающее способ хранения данных сеанса:
  • Off = 0 - сеанс не используется
  • InProc = 1 - данные сеанса хранятся в памяти домена
  • StateServer = 2 - сеанс хранится в специальной службе Windows под названием ASP.NET State Service
  • SQLServer = 3 - сеанс хранится в базе данных SQL Server
  • Custom = 4 - сеанс хранится на основе нестандартного поставщика
SessionID Строка, содержащая уникальный идентификатор сеанса. Только для чтения
Timeout Максимальный срок простоя сеанса в минутах. Для чтения и записи
Abandon() Отменяет текущий сеанс
Add(string name, object value) Добавляет новый элемент в коллекцию сеанса. Допускается альтернативный синтаксис в виде индексатора
Clear() Очищает коллекцию сеанса
RemoveAll() Очищает коллекцию сеанса
Remove(string name) Удаляет элемент данных из коллекции сеанса по ключевому имени
RemoveAt(int index) Удаляет элемент данных из коллекции сеанса по ключевому индексу

Начальное состояние сеанса устанавливается в конфигурационном файле web.config в секции <sessionState>. Настройки секции <sessionState> поддерживаются классом System.Web.Configuration.SessionStateSection через его свойства, но имена свойств в кофигурационном файле начинаются с символа нижнего регистра. Вот некоторые наиболее важные из них

Некоторые свойства SessionStateSection
Свойство Описание
Cookieless Задает режим использования cookie-набора для пересылки идентификатора сеанса. Определяется значениями перечисления System.Web.HttpCookieMode:
  • UseUri = 0 - никогда не использовать cookie-наборы, а включать идентификатор сеанса в URL (в файле web.config допускаются варианты синтаксиса: cookieless="UseUri" или cookieless="true" )
  • UseCookies = 1 - всегда использовать cookie-наборы, даже если броузер их не поддерживает или эта функциональность в нем отключена. Этот режим установлен по умолчанию. Для неподдерживающих cookie-наборы броузеров данные сеанса будут теряться, поскольку идентификатор сеанса возвращаться на сервер не будет
  • AutoDetect = 2 - наиболее надежный режим, в котором сервер сам определяет возможности броузера по поддержке куков и автоматически применяет нужной вариант пересылки идентификатора сеанса
  • UseDeviceProfile = 3 - способ пересылки идентификатора сеанса основывается только на потенциальной функциональности броузера по поддержке cookie-наборов, но без учета возможности фактического отключения этой функциональности пользователем
Mode Задает одно из значений перечисления SessionStateMode, указывающее способ хранения данных сеанса:
  • Off = 0 - указывает, что сеанс не задействуется для хранения данных. Такой режим может существенно увеличить быстродействие, когда необходимости в сеансе нет
  • InProc = 1 - данные сеанса хранятся в памяти домена
  • StateServer = 2 - сеанс хранится в специальной службе Windows под названием ASP.NET State Service
  • SQLServer = 3 - сеанс хранится в базе данных SQL Server
  • Custom = 4 - сеанс хранится на основе нестандартного поставщика
Timeout Устанавливают время простоя сеанса в минутах
RegenerateExpiredSessionId Булева переменная, запрещающая ASP.NET использовать поступивший от клиента идентификатор сеанса с просроченной меткой времени, а генерирующая новый. Это особенно важно при включенном режиме cookieless="true", когда идентификатор сеанса пересылается броузером в составе URL, куда его может специально встроить злонамеренный пользователь
CookieName Устанавливает имя идентификатора сеанса в cookie-наборе. По умолчанию используется имя "ASP.NET_SessionID"

При включенном режиме cookieless="true" броузер автоматически встраивает идентификатор сеанса в URL всех относительных ссылок. Это же справедливо и при автоматической переадресации на новую страницу методом Response.Redirect("Относительный_адрес") с полным циклом. Для того, чтобы указать системе, что идентификатор сеанса должен передаваться секретным протоколом, нужно установить Response.Cookies["ASP.NET_SessionID"].Secure = true;

Приведем пример использования сеанса.

Упражнение 10

  • Отредактируйте файл web.config приложения как показано ниже
    <?xml version="1.0" encoding="utf-8"?>
    <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
        <system.web>
            <compilation debug="true" />
            <sessionState cookieless="true" regenerateExpiredSessionId="true" />
    	</system.web>
    </configuration>
  • Добавьте страницу с именем SessionCookieless.aspx с совмещенным кодом, которую заполните так
    <%@ Page Language="C#" EnableViewState="false" %>
        
    <script runat="server">
        
        int clicks = 1;
        const int maxCol=2;
        HtmlTable table;
        
        protected void Page_Load(object sender, EventArgs e)
        {
            string[] headers = {
                    // Имена свойств секции sessionState 
                    "Cookieless",
                    "Mode",
                    "Timeout",
                    "RegenerateExpiredSessionId",
                    "CookieName",
                    // Дополнительная информация
                    "SessionID",
                    "numRequest",
                    "currentTime"
                                };
        
            // Создаем объекты пользовательского интерфейса
            // Текстовая метка заголовка
            Label label = new Label();
            form1.Controls.Add(label);
            label.Text = "<h2>Использование сеансов Session</h2>";
        
            if (!IsPostBack)
            {
                // Получаем всю информацию о параметрах конфигурации
                System.Configuration.Configuration config;
                config =
                    System.Web.Configuration.WebConfigurationManager.
                    OpenWebConfiguration(this.Request.ApplicationPath);
        
                // Объявляем ссылку на объект SessionStateSection
                System.Web.Configuration.SessionStateSection sessionState;
        
                // Поиск элемента <sessionState> в элементе <system.web>
                sessionState = (System.Web.Configuration.SessionStateSection)
                    config.GetSection(@"system.web/sessionState");
                //sessionState.Timeout = TimeSpan.FromMinutes(1);
        
                // Формируем таблицу и сохраняем ее в сеансе
                table = new HtmlTable();
                table.Border = 1;
                for (int i = 0; i < headers.Length; i++)
                {
                    // Создаем строку
                    HtmlTableRow row = new HtmlTableRow();
                    table.Rows.Add(row);
                    // Создаем ячейки в строке
                    for (int j = 0; j < maxCol; j++)
                    {
                        HtmlTableCell col = new HtmlTableCell();
                        row.Cells.Add(col);
                        if (j == 0)// Левый столбец
                        {
                            col.InnerHtml = headers[i] + ":&nbsp;";
                            col.Align = "right";
                            col.BgColor = "Yellow";// Цвет фона
                        }
                        else // Правый столбец
                        {
                            // Применяем методику отражения типов
                            System.Type type = typeof(System.Web.Configuration.
                                SessionStateSection);
                            // Извлекаем публичное свойство по его имени
                            System.Reflection.PropertyInfo property =
                                type.GetProperty(headers[i]);
                            if (property != null)
                            {
                                col.InnerHtml = "&nbsp;";
                                // Извлекаем значение свойства из экземпляра класса 
                                col.InnerHtml += property.GetGetMethod().
                                    Invoke(sessionState, null).ToString();
                            }
                            else
                            {
                                // Маркируем ячейку, чтобы потом найти
                                col.ID = headers[i];
                            }
                            col.BgColor = "#CCFFFF";// Цвет фона
                        }
                    }
                }
        
                // Сохраняем информацию в сеансе при первом запросе
                this.Session.Add("table", table);
                this.Session["clicks"] = clicks;
            }
            else
            {
                // Извлекаем информацию из сеанса при каждой обратной отсылке
                table = (HtmlTable)this.Session["table"];
                clicks = (int)this.Session["clicks"];
                // Сохраняем новое значение
                this.Session["clicks"] = ++clicks;
            }
        
            // Добавляем таблицу на форму
            form1.Controls.Add(table);
        
            // Корректируем таблицу
            ((HtmlTableCell)Page.FindControl("SessionID")).InnerHtml =
                "&nbsp;" + this.Session.SessionID;
            ((HtmlTableCell)Page.FindControl("numRequest")).InnerHtml =
                "&nbsp;" + clicks.ToString();
            ((HtmlTableCell)Page.FindControl("currentTime")).InnerHtml =
                "&nbsp;" + DateTime.Now.ToLocalTime();
        
            // Новая строка HTML
            HtmlGenericControl br = new HtmlGenericControl();
            br.InnerHtml = "<br />";
            form1.Controls.Add(br);
        
            // Кнопка Submit
            Button submit = new Button();
            form1.Controls.Add(submit);
            submit.Text = "Отправить";
        }
    </script>
        
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server" style="text-align: center;">
        </form>
    </body>
    </html>

Клиентский результат будет таким

Обратите внимание, что идентификатор сеанса встроен в URL при значении параметра Cookieless="UseUri". Отметьте также, что интерфейсная часть страницы пустая - все создается динамически. Часть выводимой в таблицу информации мы добыли из объекта класса SessionStateSection, который создается по настройкам конфигурационного файла. Для более лаконичного кода мы применили методику отражения типов, позволившую опосредованно вызывать свойства объекта. В сессиях можно сохранять объекты любого типа, а не только строки. Но при извлечении их нужно явно приводить к соответствующему типу.

Сохранение данных в состоянии приложения

Состояние приложения - это область памяти сервера, выделяемая при запуске приложения для хранения глобальных данных, доступных с любой страницы приложения. На эти данные в каждой странице ссылается объект-словарь Application типа System.Web.HttpApplicationState. Как и при состоянии сеанса, состояние приложения хранит данные в виде объектов, поэтому при их извлечении необходимо явное преобразование типов.

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