Как узнать, запущен ли Page_PreRender?

Я запускаю метод в переопределенной странице OnUnload, но только если метод Page_PreRender запущен.

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

Любые идеи?

Спасибо за любые мысли.

ОБНОВЛЕНИЕ: Позвольте мне немного перефразировать мой вопрос. Я ищу ответ на вопрос, есть ли простой способ, присущий жизненному циклу страницы, возможно, свойство, установленное фреймворком ASP.Net, возможно, что-то еще, что отличается после запуска Page_PreRender по сравнению с тем, когда Page_PreRender не работать.

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


person Mark A Johnson    schedule 19.05.2011    source источник
comment
Я несколько раз обновлял свой ответ. Я не знаю, предупредит ли вас Stack-O, когда это произойдет. Взгляни, пожалуйста. Спасибо. (О, и, пожалуйста, дайте мне знать, если есть лучший способ передать это, спасибо).   -  person MikeTeeVee    schedule 28.05.2011


Ответы (7)


Вы упоминаете (в своих комментариях к другому сообщению), что ваша проблема проявляется при вызове Response.Redirect(), потому что он вызывает исключение ThreadAbortException, что приводит к тому, что ваше событие OnPreRender() не вызывается. Так почему бы не использовать это вместо этого?:

Response.Redirect("~/SomePage.aspx", false);

«Ложь», которую вы там видите, указывает, должно ли выполнение страницы прекратиться прямо здесь и сейчас. По умолчанию Response.Redirect() использует "true". Если вам нужно, чтобы ваше событие OnPreRender() запускалось так, чтобы ваше событие OnLoad() имело все, что ему нужно, установите для него значение «false» и просто убедитесь, что вы либо переходите в конец вашего Page_Load() после вызова Response.Redirect () или что код, который будет выполняться после того, как его можно запустить.

Возможно, вам не нравится идея передачи «false» с помощью перегруженного метода Response.Redirect(), поэтому вы не пошли по этому пути. Вот некоторая документация, которая может помочь вам изменить свое мнение:

Microsoft заявляет, что «рекомендуется передавать false для параметра endResponse», поскольку указание «true» вызывает метод HttpResponse.End() для исходного запроса, который затем создает исключение ThreadAbortException. когда он завершится. Далее Microsoft заявляет, что «это исключение отрицательно влияет на производительность веб-приложений». См. здесь, в разделе «Примечания»: http://msdn.microsoft.com/en-us/library/a8wa7sdt.aspx

Это было опубликовано в прошлом году на MSDN:

Метод End также находится в моем списке «никогда не использовать». Лучший способ остановить запрос — вызвать HttpApplication.CompleteRequest. Метод End существует только потому, что мы пытались обеспечить совместимость с классическим ASP, когда была выпущена версия 1.0. Классический ASP имеет метод Response.End, который завершает обработку сценария ASP. Чтобы имитировать такое поведение, метод End ASP.NET пытается вызвать исключение ThreadAbortException. В случае успеха вызывающий поток будет прерван (очень затратно, плохо для производительности), и конвейер перейдет к событию EndRequest. ThreadAbortException, в случае успеха, конечно, означает, что поток завершается до того, как он сможет вызвать какой-либо другой код, поэтому вызов End означает, что после этого вы не будете вызывать какой-либо код. Если метод End не может сгенерировать исключение ThreadAbortException, он вместо этого сбрасывает байты ответа клиенту, но делает это синхронно, что очень плохо для производительности, и когда пользовательский код после End выполняется, конвейер перескакивает вперед к уведомлению EndRequest. Запись байтов клиенту — очень затратная операция, особенно если клиент находится на полпути к миру и использует модем 56k, поэтому лучше отправлять байты асинхронно, что мы и делаем, когда запрос завершается обычным образом. Синхронная промывка - это очень плохо. Таким образом, вы не должны использовать End, но вполне можете использовать CompleteRequest. В документации для End должно быть указано, что CompleteRequest — это лучший способ пропустить уведомление EndRequest и выполнить запрос.

Я добавил эту строку после вызова Response.Redirect(), как предлагает MSDN, и заметил, что все работает одинаково. Не уверен, что это нужно с 4.0, но я не думаю, что это повредит:

HttpContext.Current.ApplicationInstance.CompleteRequest();

Обновление 1

Использование «false» в вызове Response.Redirect() позволяет избежать ThreadAbortException, но как насчет других необработанных исключений, которые могут возникнуть на вашей странице? Эти исключения по-прежнему будут вызывать вашу проблему с вызовом OnUnload() без OnPreRender(). Вы можете использовать флаг в OnPreRender(), поскольку все предлагают избежать этого, но если вы выбрасываете необработанные исключения, у вас большие проблемы, и в любом случае вы должны перенаправлять на страницу с ошибкой. Поскольку необработанные исключения — это не то, что вы планируете создавать при каждой обратной передаче, было бы лучше, если бы вы обернули свою логику OnUnload() в Try-Catch. Если вы регистрируете и отслеживаете свои исключения, вы увидите, что необработанное исключение было выдано прямо перед регистрацией исключения NullReference Exception в событии OnUnload(), и будете знать, какое из них игнорировать. Поскольку у вашего OnUnload() будет Try-Catch, он будет безопасно продолжать обработку остальной части страницы, чтобы вы могли перенаправить на страницу с ошибкой, как и ожидалось.

Обновление 2

Вы по-прежнему должны иметь свой OnUnload(), завернутый в Try-Catch, но я думаю, что это то, что вы действительно ищете (помните, что IsRequestBeingRedirected будет истинным при вызове Response.Redirect или при перенаправлении на страницу ошибки после необработанного исключения) .:

if (HttpContext.Current.Response.IsRequestBeingRedirected != true)
{
    //You're custom OnUnload() logic here.
}

Благодаря этому вы узнаете, безопасно ли (и даже стоит ли) обрабатывать вашу пользовательскую логику в событии OnUnload(). Я понимаю, что, вероятно, мне следовало начать с этого, но я думаю, что сегодня мы многому научились. ;)

ПРИМЕЧАНИЕ. Использование Server.Transfer() также вызовет ужасный Response.End(). Чтобы избежать этого, используйте Server.Execute() с атрибутом preserveForm, для которого установлено значение «false»:

Server.Execute("~/SomePage.aspx", false);
return;

ПРИМЕЧАНИЕ. Что касается Server.Execute("~/SomePage.aspx", false); заключается в том, что IsRequestBeingRedirected будет ложным, но ваш OnPreRender() все равно будет выполняться, так что не беспокойтесь.

person MikeTeeVee    schedule 28.05.2011
comment
Это дает мне много, чтобы продолжать, так что спасибо. Я все еще представляю, что есть что-то другое в Page или HttpContext или другом связанном объекте, когда Page_PreRender вызывается, а когда нет. Я не думал, что будет логическое значение HasPreRenderRun, но что-то. Спасибо за очень вдумчивый ответ. - person Mark A Johnson; 31.05.2011

Ответ: да, можно, но не всегда :) Согласно коду Reflection, ScriptManager содержит приватное логическое поле _preRenderCompleted, для которого установлено значение true при обработке внутреннего IPage события интерфейса PagePreRenderComplete. Вы можете использовать Reflection, чтобы получить это поле из ScriptManager.GetCurrent(page) результирующий объект

person VMAtm    schedule 31.05.2011

Я не уверен, что именно вы имеете в виду под этим. В соответствии с жизненным циклом страницы ASP.NET PreRender всегда запускается до Unload. Если вы выполняете некоторое условие if внутри этого события PreRender и хотите проверить в Unload, было ли выполнено условие, логическое поле в классе страницы кажется хорошей идеей.

person Darin Dimitrov    schedule 19.05.2011
comment
Например, если вы используете Response.Redirect или Server.Transfer, вы замкнете Page_PreRender, и он не запустится. - person Mark A Johnson; 19.05.2011

Добавьте trace=true в директиву страницы.

person IrishChieftain    schedule 19.05.2011
comment
В моем случае иногда я замыкаю Page_PreRender с помощью Response.Redirect. В этом случае я получаю трассировку только со страницы, на которую я перенаправился, а не с интересующей меня страницы. - person Mark A Johnson; 19.05.2011
comment
Сохранить логический флаг в сеансе? - person IrishChieftain; 19.05.2011
comment
Это то, что я сейчас делаю. Искал что-то, возможно, в модели страницы, что сказало бы мне, что Page_PreRender запущен. - person Mark A Johnson; 20.05.2011
comment
Изучите жизненный цикл страницы, и вы найдете ответ — как заметил Дарин, эти методы всегда выполняются в определенном порядке. - person IrishChieftain; 20.05.2011
comment
Хм. Может быть, вы можете указать, что мне не хватает? Помните, моя цель — узнать в OnUnload, был ли запущен Page_PreRender. Поскольку Response.Redirect генерирует исключение ThreadAbortException, при использовании Response.Redirect где-то перед Page_PreRender Page_PreRender не запускается, а OnUnload запускается. Мне просто нужно иметь возможность сказать, когда я доберусь до OnUnload, запустился ли Page_PreRender. - person Mark A Johnson; 23.05.2011

Задайте логическое поле в обработчике событий PreRender, а затем проверьте, установлено ли оно в обработчике событий Unload.

person Dennis Traub    schedule 24.05.2011
comment
Ага. Это был мой комментарий к предложению IrishChieftain. Уже делаю. Ищите что-то более автоматическое. Спасибо. - person Mark A Johnson; 24.05.2011

Создайте пользовательское событие, которое срабатывает в событии PreRender.

person CheckRaise    schedule 24.05.2011
comment
Хм. И затем это устанавливает логическое значение на странице, возможно, говорящее, что Page_PreRender запущен? - person Mark A Johnson; 25.05.2011

Я не думаю, что сохраняется какое-либо состояние, потому что механизм ASP.NET на самом деле не нуждается в этом, поскольку он неявно знает свое состояние.

При поиске с помощью .NET Reflector кажется, что события рендеринга страницы возникают из этого внутреннего метода System.Web.UI.Page:

private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint)

Вы можете взглянуть на это, здесь нет понятия состояния. Единственная информация, которую вы можете получить, это след. Если у вас есть доступ к событию Unload, то у вас должен быть доступ к трассировке? или я что-то пропустил :-)

Поскольку трассировка на самом деле является скрытым набором данных (см. мой ответ здесь: Запись данных из Trace.axd в текстовый/xml-файл), возможно, вы сможете получить информацию. Но установка trace=true не рекомендуется в производстве...

person Simon Mourier    schedule 28.05.2011