Реализация поставщика хранилища состояний сеансов

Обновлен: Ноябрь 2007

В разделе описывается реализация пользовательского поставщика хранилища состояний сеансов и демонстрируется пример реализации поставщика.

Состояние сеанса ASP.NET предназначено для хранения пользовательских данных сеанса в различных источниках. По умолчанию информация о состоянии сеанса и соответствующие значения хранятся в памяти внутри процесса ASP.NET. Один из альтернативных вариантов заключается в использовании сервера состояний, на котором данные сеанса хранятся в отдельном процессе и сохраняются на диск при завершении работы или перезапуске приложения ASP.NET. Другой вариант заключается в хранении данных сеанса в базе данных SQL Server, что позволяет совместно использовать их нескольким веб-серверам.

Можно использовать хранилища состояний сеанса, поставляемые с ASP.NET, либо реализовать собственный поставщик хранилища состояний сеанса. Ниже перечислены причины, по которым может потребоваться собственный поставщик хранилища состояний сеанса.

  • Данные о состоянии сеанса требуется хранить в источнике данных, отличном от SQL Server, например в базе данных FoxPro или Oracle.

  • Управление данными о состоянии сеанса осуществляется с использованием схемы базы данных, отличной от схемы, используемой в поставщиках, поставляемых с платформой .NET Framework. В качестве примера можно привести данные покупательской корзины, которые хранятся с предопределенной схемой в существующей базе данных SQL Server.

Чтобы реализовать собственный поставщик хранилища состояний сеанса, создайте класс, производный от класса SessionStateStoreProviderBase. Дополнительные сведения см. ниже в подразделе "Необходимые классы".

Модуль состояния сеанса

Управление состоянием сеанса осуществляется с помощью класса SessionStateModule, в котором в различные моменты времени при выполнении запроса для чтения и записи данных сеанса в хранилище данных вызывается поставщик хранилища состояний сеанса. В начале запроса экземпляр класса SessionStateModule извлекает данные из источника данных путем вызова метода GetItemExclusive, либо, если атрибуту страницы EnableSessionState присвоено значение ReadOnly, путем вызова метода GetItem. В конце запроса, если значения состояния сеанса были изменены, экземпляр класса SessionStateModule вызывает метод SessionStateStoreProviderBase.SetAndReleaseItemExclusive, чтобы записать обновленные значения в хранилище состояний сеанса. при вызове метода HttpSessionState.Abandon модуль SessionStateModule вызывает дополнительные члены реализации класса SessionStateStoreProviderBase для инициализации нового сеанса, а также для удаления данных сеанса из источника данных. Каждый член класса SessionStateStoreProviderBase подробно описан ниже в подразделе "Необходимые классы".

Класс SessionStateModule определяет значение SessionID самостоятельно, не используя поставщик хранилища состояний сеанса. Если необходимо, можно реализовать собственный диспетчер SessionIDManager, создав класс, производный от интерфейса ISessionIDManager. Дополнительные сведения см. в подразделе "Примечания" раздела ISessionIDManager.

Для получения доступа к защищенным ресурсам, например к серверам баз данных модуль SessionStateModule обращается к удостоверению процесса ASP.NET. Можно указать, чтобы экземпляр модуля SessionStateModule олицетворял удостоверение, предоставляемое службами IIS, присвоив атрибуту useHostingIdentity элемента конфигурации <sessionState> значение false. Например, если приложение IIS настроено на использование встроенной системы безопасности Windows и при этом требуется, чтобы система ASP.NET олицетворяла удостоверение, предоставляемое службами IIS для управления сеансами, укажите в разделе конфигурации <system.web> файла Web.config приложения значение <identity impersonate="true" /> и присвойте атрибуту useHostingIdentity элемента конфигурации <sessionState> значение false. Если атрибуту useHostingIdentity присвоено значение true, то при подключении к источнику данных ASP.NET будет олицетворять удостоверение процесса или учетные данные пользователя, предоставленные элементу конфигурации <identity> (если такой элемент существует). Дополнительные сведения об удостоверении процесса ASP.NET см. в разделах Настройка удостоверения процесса ASP.NET и Олицетворение ASP.NET.

Блокирование данных в хранилище сеанса

Приложения ASP.NET являются многопоточными, что позволяет им реагировать на несколько запросов одновременно. Несколько потоков могут одновременно попытаться получить доступ к одним и тем же данным сеанса. Рассмотрим ситуацию, при которой несколько фреймов из набора рамок ссылаются на веб-страницы ASP.NET одного приложения. Отдельные запросы каждого фрейма в наборе могут параллельно выполняться на веб-сервере в разных потоках. Если страницы ASP.NET для каждого фрейма обратятся к переменным состояния сеанса, это может привести к параллельному обращению нескольких потоков к хранилищу сеанса. Чтобы избежать конфликтов данных в хранилище сеанса и непредсказуемого изменения состояния сеанса, в классах SessionStateModule и SessionStateStoreProviderBase предусмотрены функции монопольной блокировки элемента хранилища для определенного сеанса на время выполнения страницы ASP.NET. Обратите внимание, что элемент хранилища сеанса не блокируется, если атрибуту EnableSessionState присвоено значение ReadOnly. Однако при этом другие страницы ASP.NET в том же приложении могут записывать данные в хранилище сеанса, поэтому при запросе из хранилища доступных только для чтения данных сеанса по-прежнему может возникнуть ситуация, при которой необходимо будет дождаться освобождения заблокированных данных.

Данные хранилища сеанса блокируются в начале запроса при вызове метода GetItemExclusive. После выполнения запроса блокировка снимается при вызове метода SetAndReleaseItemExclusive.

Если при вызове метода GetItemExclusive или GetItem экземпляр класса SessionStateModule обнаруживает заблокированные данные сеанса, он начинает повторно запрашивать эти данные с интервалом в полсекунды до тех пор, пока блокировка не будет снята либо пока не истечет время, указанное в свойстве ExecutionTimeout. При истечении времени ожидания запроса объект SessionStateModule вызывает метод ReleaseItemExclusive, чтобы освободить данные хранилища сеанса и сразу же запросить эти данные.

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

Удаление из хранилища просроченных данных сеанса

При вызове для сеанса метода Abandon данные для этого сеанса удаляются из хранилища путем вызова метода RemoveItem. В противном случае данные остаются в хранилище данных сеанса для обслуживания будущих запросов для сеанса.

Механизм удаления просроченных данных сеанса зависит от возможностей конкретного источника данных. Если источник данных можно настроить на удаление просроченных данных сеанса в соответствии со значением свойства сеанса Timeout, можно воспользоваться методом SetItemExpireCallback, чтобы указать делегата для события Session_OnEnd и вызвать это событие при удалении просроченных данных.

Свойство ApplicationName

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

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

SELECT * FROM Sessions 
  WHERE SessionID = 'ABC123' AND ApplicationName = 'MyApplication'

В качестве уникального идентификатора элемента в хранилище состояний сеанса также можно использовать сочетание идентификатора сеанса и имени приложения.

Необходимые классы

Чтобы реализовать поставщик хранилища состояний сеанса, создайте класс, производный от абстрактного класса SessionStateStoreProviderBase. В свою очередь, класс SessionStateStoreProviderBase является производным от абстрактного класса ProviderBase, поэтому также необходимо реализовать все необходимые члены класса ProviderBase. В приведенных ниже таблицах перечислены все необходимые для реализации свойства и методы абстрактных классов ProviderBase и SessionStateStoreProviderBase; также приведено описание каждого свойства и метода. Реализацию каждого члена класса см. в разделе Пример поставщика хранилища состояния сеанса.

Необходимые члены класса ProviderBase

Член

Описание

Метод Initialize

Принимает в качестве входных параметров имя поставщика и экземпляр коллекции параметров конфигурации NameValueCollection. Этот метод используется для присвоения значений свойствам экземпляра поставщика, включая значения, характерные для конкретной реализации, и параметры, указанные в файле конфигурации (Machine.config или Web.config).

Необходимые члены класса Required SessionStateStoreProvider

Член

Описание

Метод InitializeRequest

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

Метод EndRequest

Принимает в качестве входного параметра экземпляр класса HttpContext для текущего запроса и выполняет все действия по очистке, необходимые для конкретного поставщика хранилища состояний сеанса.

Метод Dispose

Освобождает ресурсы, не используемые более поставщиком хранилища состояний сеанса.

Метод GetItemExclusive

Принимает в качестве входных параметров экземпляр класса HttpContext для текущего запроса и значение SessionID для текущего запроса. Загружает значения сеанса и данные из хранилища данных сеанса, блокирует на время выполнения запроса данные элемента сеанса в хранилище данных. Метод GetItemExclusive присваивает значения нескольким выходным параметрам, через которые вызывающий модуль SessionStateModule получает сведения о состоянии текущего элемента состояния сеанса в хранилище данных.

Если в хранилище данных не удалось найти данные об элементе сеанса, метод GetItemExclusive присваивает выходному параметру locked значение false и возвращает null. Это приводит к тому, что модуль SessionStateModule вызывает метод CreateNewStoreData, чтобы создать для запроса новый объект SessionStateStoreData.

Если данные об элементе сеанса в хранилище данных найдены, но заблокированы, метод GetItemExclusive присваивает выходному параметру locked значение true, выходному параметру lockAge — текущую дату и время за вычетом даты и времени блокировки элемента, выходному параметру lockId — идентификатор блокировки, загруженный из хранилища данных, и возвращает значение null. При этом модуль SessionStateModule повторно вызывает метод GetItemExclusive с интервалом в полсекунды, пытаясь получить данные об элементе сеанса и заблокировать данные. Если присвоенное выходному параметру значение lockAge превышает значение параметра ExecutionTimeout, модуль SessionStateModule вызывает метод ReleaseItemExclusive, чтобы снять блокировку данных элемента сеанса, после чего повторно вызывает метод GetItemExclusive.

Параметр actionFlags используется для сеансов, для которых свойству Cookieless присвоено значение true, если атрибуту regenerateExpiredSessionId присвоено значение true. Если значение параметра actionFlags равно InitializeItem(1), это указывает на то, что запись в хранилище данных сеанса соответствует новому сеансу, который необходимо инициализировать. Неинициализированные записи в хранилище данных сеанса создаются путем вызова метода CreateUninitializedItem. Если элемент из хранилища данных сеанса уже инициализирован, параметру actionFlags присваивается нулевое значение.

Если поставщик поддерживает сеансы без использования файлов cookie, присвойте выходному параметру actionFlags значение, полученное из хранилища данных сеанса для текущего элемента. Если значение параметра actionFlags для запрашиваемого элемента хранилища сеанса равно значению перечисления InitializeItem (1), то в методе GetItemExclusive после присвоения значения параметру actionFlagsout необходимо обнулить значение в хранилище данных.

Метод GetItem

Этот метод выполняет те же действия, что и метод GetItemExclusive, за исключением того, что он не блокирует элемент сеанса в хранилище данных. Метод GetItem вызывается, если атрибуту EnableSessionState присваивается значение ReadOnly.

Метод SetAndReleaseItemExclusive

Принимает в качестве входных параметров экземпляр класса HttpContext для текущего запроса, значение SessionID для текущего запроса, объект SessionStateStoreData, в котором хранятся текущие значения сеанса, которые необходимо сохранить, идентификатор блокировки для текущего запроса и значение, указывающее на то, предназначены ли сохраняемые данные для нового или существующего сеанса.

Если параметру newItem присвоено значение true, метод SetAndReleaseItemExclusive вставляет в хранилище данных новый элемент с указанными значениями. В противном случае существующему в хранилище данных элементу присваиваются указанные значения, а блокировка данных снимается. Обратите внимание, что данные сеанса обновляются только для текущего приложения, которое соответствует указанным значениям SessionID и идентификатора блокировки.

После вызова метода SetAndReleaseItemExclusive модуль SessionStateModule вызывает метод ResetItemTimeout, чтобы обновить дату и время истечения срока действия для данных элемента сеанса.

Метод ReleaseItemExclusive

Принимает в качестве входных параметров экземпляр класса HttpContext для текущего запроса, значение SessionID для текущего запроса и идентификатор блокировки для текущего запроса, после чего снимает блокировку элемента в хранилище данных сеанса. Этот метод вызывается, если при вызове метода GetItem или GetItemExclusive в хранилище данных указано, что запрошенный элемент заблокирован, но время блокировки превысило значение ExecutionTimeout. Этот метод снимает блокировку, освобождая элемент для использования другими запросами.

Метод RemoveItem

Принимает в качестве входных параметров экземпляр класса HttpContext для текущего запроса, значение SessionID для текущего запроса и идентификатор блокировки для текущего запроса, после чего удаляет данные сеанса из хранилища данных, где элемент хранилища данных соответствует указанному значению SessionID, текущему приложению и предоставленному идентификатору блокировки. Этот метод вызывается при вызове метода Abandon.

Метод CreateUninitializedItem

Принимает в качестве входных параметров экземпляр класса HttpContext для текущего запроса, значение SessionID для текущего запроса и идентификатор блокировки для текущего запроса, после чего добавляет в хранилище данных запроса неинициализированный элемент со значением actionFlags параметра InitializeItem.

Метод CreateUninitializedItem используется в сеансах без файлов cookie, если атрибуту regenerateExpiredSessionId присвоено значение true; при этом модуль SessionStateModule создает новое значение SessionID при обнаружении просроченного идентификатора сеанса.

В процессе создания нового значения SessionID требуется, чтобы обозреватель был перенаправлен на URL-адрес, содержащий вновь созданный идентификатор сеанса. Метод CreateUninitializedItem вызывается во время начального запроса с просроченным идентификатором запроса. После того, как объект SessionStateModule получает новое значение SessionID для замены просроченного идентификатора сеанса, он вызывает метод CreateUninitializedItem, чтобы добавить неинициализированную запись в хранилище данных состояний сеанса. Затем веб-обозреватель перенаправляется по URL-адресу, содержащему созданное новое значение SessionID. Наличие неинициализированной записи в хранилище данных сеанса гарантирует, что перенаправленный запрос с созданным новым значением SessionID не будет по ошибке считаться запросом просроченного сеанса, а будет считаться запросом нового сеанса.

Неинициализированная запись в хранилище данных сеанса связана с созданным новым значением SessionID и содержит только значения по умолчанию, включая дату и время истечения срока действия, а также значение, соответствующее параметру actionFlags методов GetItem и GetItemExclusive. Неинициализированная запись в хранилище состояний сеанса должна включать значение actionFlags, совпадающее со значением перечисления InitializeItem (1). Это значение передается модулю SessionStateModule при вызове методов GetItem и GetItemExclusive и позволяет модулю SessionStateModule определить, что текущий сеанс является новым. Затем модуль SessionStateModule инициализирует новый сеанс и вызывает событие Session_OnStart.

Метод CreateNewStoreData

Принимает в качестве входных параметров экземпляр HttpContext для текущего запроса и значение Timeout для текущего сеанса, после чего возвращает новый объект SessionStateStoreData с пустым объектом ISessionStateItemCollection, коллекцию HttpStaticObjectsCollection и указанное значение Timeout. Экземпляр коллекции HttpStaticObjectsCollection для приложения ASP.NET можно получить, вызвав метод GetSessionStaticObjects.

Метод SetItemExpireCallback

Принимает в качестве входного параметра делегата, который ссылается на событие Session_OnEnd, заданное в файле Global.asax. Если поставщик хранилища состояний сеанса поддерживает событие Session_OnEnd, задается локальная ссылка на параметр SessionStateItemExpireCallback, а метод возвращает значение true; в противном случае метод возвращает значение false.

Пример поставщика

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

См. также

Основные понятия

Пример поставщика хранилища состояния сеанса

Общие сведения о состоянии сеанса ASP.NET

Общие сведения об управлении состоянием ASP.NET