"HttpContext.Current.Session" против Global.asax "this.Session" - PullRequest
13 голосов
/ 21 января 2009

Недавно во время работы над кодом для проекта ASP.NET на работе. Нам потребовался инструмент отслеживания для определения основных показателей активности пользователей (количество посещений страниц и т. Д.), Мы отслеживали их в Session, а затем сохраняли данные в БД с помощью Session_End в Global.asax .

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

Весь код отслеживания инкапсулирован в классе Tracker, включая свойства, которые по существу обертывают переменные Session.

Проблема заключается в том, что когда я выполнял Tracker.Log() в методе Session_End, HttpContext.Current.Session в коде Tracker завершался ошибкой с NullReferenceException . Теперь это имеет смысл, поскольку HttpContext всегда относится к текущему запросу, и, конечно, в Session_End нет запроса.

Я знаю, что Global.asax имеет свойство Session, которое возвращает HttpSessionState, которое на самом деле, кажется, работает нормально (в итоге я вставил его в трекер) ..

Но мне любопытно, как, черт возьми, я могу получить такую ​​же ссылку на объект HttpSessionState, используемый Global.asax из снаружи из Global.asax?

Заранее спасибо, ребята, я ценю вклад. :)

Ответы [ 7 ]

16 голосов
/ 23 января 2009

Чтобы лучше ответить на оригинальный вопрос:

Фон

Каждый запрос одной страницы раскручивает новый объект Session, а затем раздувает его из хранилища сеансов. Для этого он использует cookie, предоставленный клиентом, или специальную конструкцию пути (для сеансов без cookie). С помощью этого идентификатора сеанса он обращается к хранилищу сеансов и десериализует (поэтому все провайдеры, кроме InProc должны быть сериализуемыми) нового объекта сеанса.

В случае провайдера InProc просто вручает вам ссылку, хранящуюся в HttpCache с ключом идентификатора сеанса. Вот почему поставщик InProc сбрасывает состояние сеанса при перезапуске AppDomain (а также почему несколько веб-серверов не могут совместно использовать состояние сеанса InProc .

Этот вновь созданный и надутый объект застрял в коллекции Context.Items, поэтому он доступен на время запроса.

Любые изменения, которые вы вносите в объект Session, затем сохраняются в конце запроса к хранилищу сеансов путем сериализации (или в случае InProc запись HttpCache обновляется).

Так как Session_End запускается без текущего запроса на лету, объект Session запускается ex-nilo, информация недоступна. При использовании состояния сеанса InProc истечение срока действия HttpCache инициирует событие обратного вызова в ваше событие Session_End, поэтому запись сеанса доступна, но все еще является копией того, что в последний раз сохранялось в HttpContext.Cache. Это значение сохраняется в свойстве HttpApplication.Session внутренним методом (называемым ProcessSpecialRequest), где оно затем доступно. Во всех остальных случаях он внутренне исходит из значения HttpContext.Current.Session.

Ваш ответ

Поскольку Session_End всегда срабатывает против нулевого контекста, вы должны ВСЕГДА использовать this.Session в этом событии и передавать объект HttpSessionState в код трассировки. Во всех других контекстах, это прекрасно, чтобы выбрать из HttpContext.Current.Session и затем передать в код трассировки. НЕ , однако, пусть код трассировки дойдет до контекста сеанса.

Мой ответ

Не используйте Session_End, если только вы не знаете, что хранилище сеансов, которое вы используете, поддерживает Session_End, которое оно делает , если возвращает true из SetItemExpireCallback. Единственный встроенный магазин - это магазин InProcSessionState. Можно написать хранилище сеансов, которое делает это, но вопрос о том, кто будет обрабатывать Session_End, является двусмысленным, если имеется несколько серверов.

10 голосов
/ 21 января 2009

Global.asax реализует HttpApplication - то, о чем вы говорите, когда вызываете это изнутри.

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

ОДНАКО

Ваше приложение может создавать несколько экземпляров HttpApplication для обработки параллельных запросов, и эти экземпляры можно использовать повторно, поэтому простое их получение не гарантирует, что у вас есть нужный.

Я бы также добавил предостережение - если ваше приложение аварийно завершает работу, нет никакой гарантии, что будет вызван session_end, и вы потеряете все данные во всех сеансах, что явно нехорошо.

Я согласен, что регистрация на каждой странице, вероятно, не очень хорошая идея, но, возможно, на полпути с какой-то асинхронной регистрацией - вы запускаете детали для класса журналирования, который то и дело регистрирует детали, которые вы ищете - все еще не на 100% надежно, если приложение вылетает, но вероятность потери всего меньше.

3 голосов
/ 21 января 2009

Я думаю, что вы уже ответили на свой вопрос: обычно свойства Session в Global.asax и HttpContext.Current.Session совпадают (если есть текущий запрос). Но в случае тайм-аута сеанса активный запрос отсутствует, и поэтому вы не можете использовать HttpContext.Current.

Если вы хотите получить доступ к сеансу из метода, вызванного Session_End, то передайте его в качестве параметра. Создайте перегруженную версию метода Log (), который принимает HttpSessionState в качестве параметра, затем вызовите Tracker.Log (this.Session) из обработчика событий Session_End.

Кстати: вы знаете, что ни в коем случае не можете рассчитывать на событие завершения сеанса? Он будет работать только до тех пор, пока у вас есть состояние сеанса в процессе. При использовании SQL-сервера или StateServer для управления состоянием сеанса событие завершения сеанса не будет запускаться.

2 голосов
/ 21 января 2011

Событие Session_End возникает только тогда, когда sessionstate mode установлено в InProc в файле Web.config. Если режим сеанса установлен на StateServer или SQLServer, событие не возникает.

используйте Session["SessionItemKey"] для получения значения сеанса.

0 голосов
/ 09 ноября 2011

Сессия доступна в вашем файле Global.asax, во время события Session_Start. Может быть, подождать до этого момента, чтобы делать вещи?

0 голосов
/ 16 июля 2009

Хорошо, у меня такая же проблема, чтобы отслеживать активность сессии. Вместо использования события session_end я реализовал интерфейс и деструктор IDisposable для моего класса sessiontracker. Я изменил метод Dispose (), чтобы сохранить активность сеанса в БД. Я вызвал метод obj.Dispose (), когда пользователь нажимает кнопку выхода из системы. Если пользователь по ошибке закрыл браузер, то GC вызовет деструктор при очистке объектов (не сразу, но наверняка через некоторое время вызовет этот метод). Метод деструктора внутренне выполняет тот же метод Dispose (), чтобы сохранить действия сеанса в БД.

-Shan

0 голосов
/ 21 января 2009

Помните, что Session_End запускается, когда время сеанса истекло без активности. Браузер не генерирует это событие (потому что оно неактивно), поэтому единственное время, когда вы действительно получите это событие, - это использование провайдера InProc. У КАЖДОГО ДРУГОГО провайдера это событие никогда не произойдет.

Мораль? Не используйте Session_End.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...