Question

I'm running a method in an overridden Page OnUnload, but only if the Page_PreRender method has run.

Obviously, I can flip a class-level bool when I'm in Page_PreRender and check it in OnUnload, but if there's a more intrinsic way to tell is Page_PreRender has run, I'd like to use that.

Any ideas?

Thanks for any thoughts.

UPDATE: Let me rephrase my question slightly. I'm looking for the answer to whether there is a simple way, inherent in the Page life cycle, perhaps a property that is set by the ASP.Net frameowork, perhaps something else, that is different after Page_PreRender has run versus when Page_PreRender has not run.

I am currently setting a boolean in Page_PreRender to tell me if it has run. It works, but I don't like this solution if there is a way to accomplish the same thing without adding the extra boolean check. Creating an event that fires during Page_PreRender is the same level of redundancy I'd like to avoid, if possible.

Was it helpful?

Solution

You mention (in your comments on another post) that your problem manifests itself when calling Response.Redirect() because it throws a ThreadAbortException, which leads to your OnPreRender() event not being called. So why not use this instead?:

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

The "false" you see there indicates if execution of the page should terminate right there and then. By default, Response.Redirect() uses "true". If you need your OnPreRender() event to run so that your OnLoad() event will have everything it needs, then set it to "false" and just make sure you either jump to the end of your Page_Load() after calling Response.Redirect() or that the code that would execute after it is fine to run.

Maybe you don't like the idea of passing "false" using the overloaded Response.Redirect() method so that's why you didn't go that route. Here is some documentation that may help sway your mind:

Microsoft states that "passing false for the endResponse parameter is recommended" because specifying "true" calls the HttpResponse.End() method for the original request, which then throws a ThreadAbortException when it completes. Microsoft goes on to say that "this exception has a detrimental effect on Web application performance". See here in the "Remarks" section: http://msdn.microsoft.com/en-us/library/a8wa7sdt.aspx

This was posted last year on MSDN:

The End method is also on my “never use” list. The best way to stop the request is to call HttpApplication.CompleteRequest. The End method is only there because we tried to be compatible with classic ASP when 1.0 was released. Classic ASP has a Response.End method that terminates processing of the ASP script. To mimic this behavior, ASP.NET’s End method tries to raise a ThreadAbortException. If this is successful, the calling thread will be aborted (very expensive, not good for performance) and the pipeline will jump ahead to the EndRequest event. The ThreadAbortException, if successful, of course means that the thread unwinds before it can call any more code, so calling End means you won’t be calling any code after that. If the End method is not able to raise a ThreadAbortException, it will instead flush the response bytes to the client, but it does this synchronously which is really bad for performance, and when the user code after End is done executing, the pipeline jumps ahead to the EndRequest notification. Writing bytes to the client is a very expensive operation, especially if the client is halfway around the world and using a 56k modem, so it is best to send the bytes asynchronously, which is what we do when the request ends the normal way. Flushing synchronously is really bad. So to summarize, you shouldn’t use End, but using CompleteRequest is perfectly fine. The documentation for End should state that CompleteRequest is a better way to skip ahead to the EndRequest notification and complete the request.

I added this line after calling Response.Redirect(), as MSDN suggests, and noticed everything appeared to run the same. Not sure if it's needed with 4.0, but I don't think it hurts:

HttpContext.Current.ApplicationInstance.CompleteRequest();

Update 1

Using "false" in the call to Response.Redirect() avoids the ThreadAbortException, but what about other Unhandled Exceptions that could be thrown on your page? Those exceptions will still cause your problem of OnUnload() being called without OnPreRender(). You can use a flag in OnPreRender() as everyone suggests to avoid this, but if you're throwing Unhandled Exceptions, you've got bigger problems and should be redirecting to an error page anyway. Since Unhandled Exceptions aren't something you plan to throw on every postback, it would be better if you wrapped your OnUnload() logic in a Try-Catch. If you're logging and monitoring your exceptions you will see that an Unhandled Exception was thrown right before logging a NullReference Exception in the OnUnload() event and will know which one to ignore. Because your OnUnload() will have a Try-Catch, it will safely continue processing the rest of the page so you can Redirect to the error page as expected.

Update 2

You should still have your OnUnload() wrapped in a Try-Catch, but I think this is what you're really looking for (remember IsRequestBeingRedirected will be true when calling Response.Redirect or when redirecting to an error page after an Unhandled Exception).:

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

With this, you will know if it is safe (or even worth it) to process your custom logic in the OnUnload() event. I realize I should have probably lead off with this, but I think we learned a lot today. ;)

NOTE: The use of Server.Transfer() will also call the dreaded Response.End(). To avoid this, use Server.Execute() with the preserveForm attribute set to "false" instead:

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

NOTE: The thing about Server.Execute("~/SomePage.aspx", false); is that IsRequestBeingRedirected will be false, but your OnPreRender() will still execute, so no worries there.

OTHER TIPS

The answer is Yes, you can, but not always :) According the Reflection code, the ScriptManager class contains the private bool field _preRenderCompleted, which is set to true while handling internal IPage interface PagePreRenderComplete event. You can use the Reflection to get this field from ScriptManager.GetCurrent(page) resulting object

I am not sure what exactly you mean by this. According to the ASP.NET Page Lifecycle PreRender always runs before Unload. If you perform some if condition inside this PreRender event and you would like to test in the Unload whether the condition was satisfied a boolean field on the page class seems a good idea.

Add trace=true to the page directive.

Set a boolean field in the PreRender event handler, then check if it was set in the Unload event handler.

Create a custom event that fires in the PreRender event.

I don't think there is any sort of state stored because the ASP.NET engine does not really need that, as it knows its state implicitely.

Searching with .NET Reflector, it seems the page render events are raised from this internal System.Web.UI.Page method:

private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint)

You can have a look at it, there is no notion of state. The only information you can get is the trace. If you have access to the Unload event, then you should have access to the trace? or I miss something :-)

Since the trace is in fact a dataset undercovers (see my answer here: Logging the data in Trace.axd to a text/xml file), you maybe could get the information. But setting trace=true is not recommended in production though...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top