“HttpContext.Current.Сессия” против Global.asax “this.Сессия”
-
19-08-2019 - |
Вопрос
Недавно, работая над некоторым кодом для ASP.NET проекта в работе.Нам нужна была утилита отслеживания, чтобы получать базовые показатели активности пользователей (количество посещений страницы и т.д.), С помощью которых мы могли бы отслеживать их Session
, затем сохраните данные в базе данных через Session_End
в Global.asax
.
Я начал взламывать, начальный код работал нормально, обновляя базу данных при каждой загрузке страницы.Однако я хотел удалить это попадание в базу данных при каждом запросе и просто полагаться на Session_End
для хранения всех данных.
Весь код отслеживания инкапсулирован в Tracker
класс, включая свойства, которые по существу обертывают переменные сеанса.
Проблема это когда я казнил Tracker.Log()
в Session_End
способ, позволяющий HttpContext.Current.Session
в коде трекера произошел сбой с NullReferenceException
.Теперь это имеет смысл, поскольку HttpContext
всегда относится к текущий запрос, и, конечно же, в Session_End
, запроса нет.
Я знаю, что Global.asax
имеет Session
свойство, которое возвращает HttpSessionState
это на самом деле, кажется, работает нормально (в итоге я ввел его в трекер)..
Но мне любопытно, как, черт возьми, я могу получить такую же ссылку на HttpSessionState
объект, используемый Global.asax
От снаружи из Global.asax
?
Заранее спасибо, ребята, я ценю ваш вклад.:)
Решение
Global.asax реализует HttpApplication - это то, с чем вы общаетесь при вызове это изнутри него.
Документация MSDN для HttpApplication Применение HttpApplication содержит подробную информацию о том, как вы можете получить доступ к нему, например, в HttpHandler, а затем получить доступ к различным его свойствам.
ОДНАКО
Ваше приложение может создавать несколько экземпляров HttpApplication для обработки параллельных запросов, и эти экземпляры могут использоваться повторно, поэтому простое их использование каким-либо образом не гарантирует, что у вас есть правильный.
Я бы тоже добавил предостережение - если ваше приложение выйдет из строя, нет гарантии, что будет вызван session_end , и вы потеряете все данные во всех сеансах, что явно не очень хорошо.
Я согласен, что ведение журнала на каждой странице, вероятно, не самая лучшая идея, но, возможно, это промежуточный этап с некоторым асинхронным ведением журнала - вы отправляете данные в класс logging, который время от времени регистрирует нужные вам данные - все равно не на 100% надежно, если приложение выйдет из строя, но у вас меньше шансов потерять все.
Другие советы
Чтобы лучше ответить на первоначальный вопрос:
Предыстория
Каждый отдельный запрос страницы запускает новый Session
объект, а затем раздувает его из вашего хранилища сеансов.Для этого он использует файл cookie, предоставленный клиентом, или специальную конструкцию path (для сеансов без использования cookies).С помощью этого идентификатора сеанса он обращается к хранилищу сеансов и десериализует (вот почему все поставщики, кроме InProc, должны быть сериализуемыми) новый объект сеанса.
В случае поставщика InProc просто передает вам ссылку, которая хранится в HttpCache
вводится по идентификатору сеанса. Вот почему поставщик InProc прерывает состояние сеанса, когда AppDomain
перерабатывается (а также почему несколько веб-серверов не могут совместно использовать состояние сеанса InProc.
Этот вновь созданный и раздутый объект застрял в Context.Items
коллекция, чтобы она была доступна в течение всего срока действия запроса.
Любые изменения, которые вы вносите в Session
объекты затем сохраняются в конце запроса в хранилище сеансов путем сериализации (или, в случае InProc, HttpCache
запись обновлена).
С тех пор как Session_End
срабатывает без текущего запроса в-fly, Session
объект развернут ex-nilo, информация недоступна.При использовании состояния сеанса InProc истечение срока HttpCache
запускает событие обратного вызова в ваш Session_End
событие, поэтому запись сеанса доступна, но по-прежнему является копией того, что было последним сохранено в HttpContext.Cache
.Это значение сохраняется в HttpApplication.Session
свойство с помощью внутреннего метода (вызываемого ProcessSpecialRequest
), где он затем будет доступен.Во всех других случаях это внутренне исходит из HttpContext.Current.Session
ценность.
Ваш ответ
Поскольку Session_End всегда запускается в контексте null, вы всегда должны использовать это.Session в этом событии и передайте объект HttpSessionState в ваш код трассировки.Во всех других контекстах совершенно нормально извлекать данные из HttpContext.Current.Session
а затем перейдите в код трассировки. НЕ ДЕЛАЙТЕ ЭТОГО, однако позвольте коду трассировки подключиться к контексту сеанса.
Мой ответ
Не используйте Session_End
если только вы не знаете, что используемое вами хранилище сеансов поддерживает Session_End
, который он делает если он вернется true
От SetItemExpireCallback
.Единственный магазин "в коробке", который делает это, - это InProcSessionState
Магазин.Можно написать хранилище сеансов, которое выполняет, но вопрос о том, кто будет обрабатывать Session_End
это несколько неоднозначно, если есть несколько серверов.
Я думаю, вы уже ответили на свой собственный вопрос:обычно свойство Session в Global.asax и HttpContext.Current.Session одинаковы (если есть текущий запрос).Но в случае тайм-аута сеанса активный запрос отсутствует, и поэтому вы не можете использовать HttpContext.Current.
Если вы хотите получить доступ к сеансу из метода, вызываемого Session_End, то передайте его в качестве параметра.Создайте перегруженную версию метода Log(), который принимает HttpSessionState в качестве параметра, затем вызовите Tracker.Регистрируйте (этот.Сеанс) из обработчика события Session_End.
КСТАТИ:вы осознаете, что ни в коем случае не можете полагаться на событие окончания сеанса?Это будет работать только до тех пор, пока у вас есть состояние сеанса в процессе.При использовании SQL server или StateServer для управления состоянием сеанса событие завершения сеанса не срабатывает.
В Session_End
событие вызывается только тогда, когда sessionstate mode
установлено значение InProc
в Web.config
файл.Если режим сеанса установлен на StateServer
или SQLServer
, событие не вызвано.
использование Session["SessionItemKey"]
чтобы получить значение сеанса.
Хорошо, у меня такая же проблема с отслеживанием активности сеанса.Вместо использования события session_end я внедрил IDisposable интерфейс и деструктор в свой класс sessiontracker.Я изменил метод Dispose(), чтобы сохранить активность сеанса в базе данных.Я вызвал метод obj.Dispose(), когда пользователь нажимает кнопку выхода из системы.Если пользователь закрыл браузер по ошибке, то GC вызовет деструктор во время очистки объектов (не сразу, но наверняка он вызовет этот метод через некоторое время).Метод деструктора внутренне выполняет тот же метод Dispose(), чтобы сохранить действия сеанса в DB.
-Шан
Сеанс доступен в вашем глобальном файле.asax во время события Session_Start.Может быть, подождать до этого момента, чтобы что-то сделать?
Помните, что Session_End запускается, когда время ожидания сеанса истекает без активности.Браузер не инициирует это событие (поскольку он неактивен), поэтому единственный раз, когда вы действительно получите событие, - это при использовании поставщика InProc.У ЛЮБОГО ДРУГОГО провайдера это событие никогда не сработает.
Моральный?Не используйте Session_End.