Вопрос

Недавно, работая над некоторым кодом для 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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top