December, 2007

  • Never doubt thy debugger

    Application, Page and Control lifecycle


    The problem

    Every now and then we receive an advisory case from a customer whom needs some advices from us about how to implement some kind of functionality, and this particular one had an interesting and weird behavior with the viewstate in a custom composite control: basically the customer implemented a sort of tabbed control which had to act as a container for other controls like checkboxes, and was meant to be used at design time within Visual Studio by other developers. The control itself was actually based on a CompositeControl and a View (and MultiView) class, and everything was apparently working fine until he tried to uncheck one of the checkboxes within the control and submit the page: after the postback the checkbox was still flagged smile_omg. The custom control was clearly the the one having the problem because another checkbox outsite it (just placed somewhere else on the page) worked as expected. It's interesting to note that the customer has customized the viewstate management and was overriding the SaveViewState and LoadViewState events, and on my experience viewstate and the chain of events which occurs in the page lifecycle are often the ones to blame for such problems...

    A quick test was trying to disable the viewstate for the checkbox:

    <cc:MyTabView ID="MyTab2" runat="server">
        <cc:MyTab ID="MyTab1" runat="server" Title="Tab 1">
            <asp:CheckBox ID="CheckBox1" Text="My checkbox" runat="server" EnableViewState="false" />

    This fixed the problem, the checkbox was behaving fine now. But that was not an acceptable solution: this custom control was meant to be used by other developers within their applications and imposing a constraint like this one, forcing developers to disable the viewstate for their contained controls could severely limit their ability to develop nice and fully functional controls. So, back to debugging.

    The first think I did was to enable tracing in my web.config to get an idea of the chain of events raised:

    Begin PreInit   
    End PreInit  
    Begin Init  
    End Init  
    Begin InitComplete  
    End InitComplete  
    Begin LoadState  
    End LoadState  
    Begin ProcessPostData  
    End ProcessPostData  
    Begin PreLoad  
    End PreLoad  
    Begin Load  
    End Load  
    Begin ProcessPostData Second Try 
    End ProcessPostData Second Try  
    Begin Raise ChangedEvents  
    End Raise ChangedEvents  
    Begin Raise PostBackEvent 
    End Raise PostBackEvent  
    Begin LoadComplete  
    End LoadComplete  
    Begin PreRender  
    End PreRender  
    Begin PreRenderComplete  
    End PreRenderComplete  
    Begin SaveState  
    End SaveState  
    Begin SaveStateComplete  
    End SaveStateComplete  
    Begin Render  
    End Render  

    As you can see when the POST data reaches the server, the runtime loads the "old" viewstate (the one saved at the previous request), then processes the posted data, then the "change" events are fired and immediately after also the postback events are fired, and after rendering the page the viewstate is saved once again ready to start the cycle again when the next request will come in. The reason to compare the control values and state posted from the client with the "old" viewstate is to find what has changed and fire the appropriate events on relevant objects and classes; specifically to checkboxes, to determine if the "checked" status has changed the runtime verifies which value the checkbox had in the "old" viewstate and confronts it with the new value coming with the posted data. In practice this means that is the checkbox value is part of the posted back data is it flagged, otherwise it is not (and the Checked event is fired accordingly).

    Back to our problem, debugging the page showed that the form values where posted back correctly, so was the checkbox value, also when reproducing the problem submitting the page the second time; well, the checkbox was not part of the posted back data but since it was unchecked that is what we are excepting, right? An once on the server side I could hook the Checked event and see that is was fired as expected. Stepping through the code even showed that while elaborating the request and building the HTML to send to the client, the checkbox was actually unchecked until around the latest LoadViewstate event... smile_omg

    Isn't that enough to point to the page and control lifecycle events?

    A bit of theory

    There are a few articles and blog posts on this subject, here is what the MSDN Library says:

    The key point is: you can do almost whatever you want but you must do it at the right time, or bad things could happen... So here's an event list (should be quite comprehensive but something may be still missing) that takes place when an HTTP request is received from the ASP.NET ISAPI till the resulting HTML is sent to the client; this is the result of a sample page which hosts a UserControl which in turn contains a CheckBox:

    HttpApplication   Based on the file name extension of the requested resource (mapped in the application's configuration file), select a class that implements IHttpHandler to process the request. If the request is for an object (page) derived from the Page class and the page needs to be compiled, ASP.NET compiles the page before creating an instance of it
    HttpApplication   Call the ProcessRequest method (or the asynchronous version IHttpAsyncHandler::BeginProcessRequest) of the appropriate IHttpHandler class for the request. For example, if the request is for a page, the current page instance handles the request
    HttpApplication   Perform response filtering if the Filter property is defined
    HttpApplication Start

    Called when the first resource (such as a page) in an ASP.NET application is requested. The Application_Start method is called only one time during the life cycle of an application. You can use this method to perform startup tasks such as loading data into the cache and initializing static values.

    You should set only static data during application start. Do not set any instance data because it will be available only to the first instance of the HttpApplication class that is created

    HttpModule BeginRequest The BeginRequest event signals the creation of any given new request. This event is always raised and is always the first event to occur during the processing of a request
    HttpModule AuthenticateRequest The AuthenticateRequest event signals that the configured authentication mechanism has authenticated the current request. Subscribing to the AuthenticateRequest event ensures that the request will be authenticated before processing the attached module or event handler
    HttpModule PostAuthenticateRequest The PostAuthenticateRequest event is raised after the AuthenticateRequest event has occurred. Functionality that subscribes to the PostAuthenticateRequest event can access any data that is processed by the PostAuthenticateRequest
    HttpModule AuthorizeRequest The AuthorizeRequest event signals that ASP.NET has authorized the current request. Subscribing to the AuthorizeRequest event ensures that the request will be authenticated and authorized before processing the attached module or event handler
    HttpModule ResolveRequestCache Occurs when ASP.NET finishes an authorization event to let the caching modules serve requests from the cache, bypassing execution of the event handler (for example, a page or an XML Web service).
    HttpModule PostResolveRequestCache Occurs when ASP.NET bypasses execution of the current event handler and allows a caching module to serve a request from the cache
    HttpModule PostMapRequestHandler Occurs when ASP.NET has mapped the current request to the appropriate event handler
    Session Start  
    HttpModule AcquireRequestState Occurs when ASP.NET acquires the current state (for example, session state) that is associated with the current request
    HttpModule PostAcquireRequestState Occurs when the request state (for example, session state) that is associated with the current request has been obtained
    HttpModule PreRequestHandlerExecute Occurs just before ASP.NET starts executing an event handler (for example, a page or an XML Web service)
    Page PreInit

    Use this event for the following:

    • Check the IsPostBack property to determine whether this is the first time the page is being processed

    • Create or re-create dynamic controls

    • Set a master page dynamically

    • Set the Theme property dynamically

    • Read or set profile property values


      If the request is a postback, the values of the controls have not yet been restored from view state. If you set a control property at this stage, its value might be overwritten in the next event.

    WebUserControl Init Initialize settings needed during the lifetime of the incoming Web request. See Handling Inherited Events
    Page Init Raised after all controls have been initialized and any skin settings have been applied. Use this event to read or initialize control properties
    Page InitComplete Use this event for processing tasks that require all initialization be complete
    Page PreLoad

    Use this event if you need to perform processing on your page or control before the Load event.

    After the Page raises this event, it loads view state for itself and all controls, and then processes any postback data included with the Request instance

    WebUserControl LoadViewState At the end of this phase, the ViewState property of a control is automatically populated as described in Maintaining State in a Control. A control can override the default implementation of the LoadViewState method to customize state restoration
    WebUserControl LoadPostData

    Process incoming form data and update properties accordingly. See Processing Postback Data.


    Only controls that process postback data participate in this phase

    Page Load

    The Page calls the OnLoad event method on the Page, then recursively does the same for each child control, which does the same for each of its child controls until the page and all controls are loaded.

    Use the OnLoad event method to set properties in controls and establish database connections

    WebUserControl Load Perform actions common to all requests, such as setting up a database query. At this point, server controls in the tree are created and initialized, the state is restored, and form controls reflect client-side data. See Handling Inherited Events
    WebUserControl RaisePostDataChangedEvent

    (if IPostBackDataHandler is implemented)

    Raise change events in response to state changes between the current and previous postbacks. See Processing Postback Data.

    Note   Only controls that raise postback change events participate in this phase
    WebUserControl RaisePostBackEvent (if IPostBackEventHandler is implemented)

    Handle the client-side event that caused the postback and raise appropriate events on the server. See Capturing Postback Events.

    Note   Only controls that process postback events participate in this phase.

    Use these events to handle specific control events, such as a Button control's Click event or a TextBox control's TextChanged event.


    In a postback request, if the page contains validator controls, check the IsValid property of the Page and of individual validation controls before performing any processing.

    Page LoadComplete Use this event for tasks that require that all other controls on the page be loaded
    Page PreRender

    Before this event occurs:

    • The Page object calls EnsureChildControls for each control and for the page.

    • Each data bound control whose DataSourceID property is set calls its DataBind method. For more information, see Data Binding Events for Data-Bound Controls below.

    The PreRender event occurs for each control on the page. Use the event to make final changes to the contents of the page or its controls

    WebUserControl PreRender Perform any updates before the output is rendered. Any changes made to the state of the control in the prerender phase can be saved, while changes made in the rendering phase are lost. See Handling Inherited Events
    Page PreRenderComplete Occurs before the page content is rendered. The PreRenderComplete event is raised when the pre-render stage of the page life cycle is complete. At this stage of the page life cycle, all controls are created, any pagination required is completed, and the page is ready to render to the output.

    This is the last event raised before the page's view state is saved

    Page SaveViewState Saves any server control view-state changes that have occurred since the time the page was posted back to the server.
    WebUserControl SaveViewState The ViewState property of a control is automatically persisted to a string object after this stage. This string object is sent to the client and back as a hidden variable. For improving efficiency, a control can override the SaveViewState method to modify the ViewState property. See Maintaining State in a Control
    Page SaveStateComplete

    Before this event occurs, ViewState has been saved for the page and for all controls. Any changes to the page or controls at this point will be ignored.

    Use this event perform tasks that require view state to be saved, but that do not make any changes to controls

    Page RenderControl Outputs server control content to a provided HtmlTextWriter object and stores tracing information about the control if tracing is enabled.

    If a server control's Visible property is set to true, this method determines whether tracing is enabled for the page. If so, it stores trace information associated with the control, and renders the server control content to the page.

    This method is automatically called by the page during the rendering, but can be overridden by custom control developers.

    Page Render

    This is not an event; instead, at this stage of processing, the Page object calls this method on each control. All ASP.NET Web server controls have a Render method that writes out the control's markup that is sent to the browser.

    If you create a custom control, you typically override this method to output the control's markup. However, if your custom control incorporates only standard ASP.NET Web server controls and no custom markup, you do not need to override the Render method. For more information, see Developing Custom ASP.NET Server Controls.

    A user control (an .ascx file) automatically incorporates rendering, so you do not need to explicitly render the control in code

    Page RenderChildren Outputs the content of a server control's children to a provided HtmlTextWriter object, which writes the content to be rendered on the client. This method notifies ASP.NET to render any Active Server Pages (ASP) code on the page. If no ASP code exists on the page, this method renders any child controls for the server control.
    WebUserControl RenderControl Outputs server control content to a provided HtmlTextWriter object and stores tracing information about the control if tracing is enabled.
    WebUserControl Render Generate output to be rendered to the client. See Rendering an ASP.NET Server Control
    WebUserControl RenderChildren Outputs the content of a server control's children to a provided HtmlTextWriter object, which writes the content to be rendered on the client. This method notifies ASP.NET to render any Active Server Pages (ASP) code on the page. If no ASP code exists on the page, this method renders any child controls for the server control.
    WebUserControl Unload Perform any final cleanup before the control is torn down. Control authors generally perform cleanup in Dispose and do not handle this event
    WebUsercontrol Dispose Perform any final cleanup before the control is torn down. References to expensive resources such as database connections must be released in this phase. See Methods in ASP.NET Server Controls
    Page Unload

    This event occurs for each control and then for the page. In controls, use this event to do final cleanup for specific controls, such as closing control-specific database connections.

    For the page itself, use this event to do final cleanup work, such as closing open files and database connections, or finishing up logging or other request-specific tasks.


    During the unload stage, the page and its controls have been rendered, so you cannot make further changes to the response stream. If you attempt to call a method such as the Response.Write method, the page will throw an exception.

    Page Dispose Enables a server control to perform final clean up before it is released from memory. (inherited from Control)
    HttpModule PostRequestHandlerExecute Occurs when the ASP.NET event handler (for example, a page or an XML Web service) finishes execution
    HttpModule ReleaseRequestState

    Occurs after ASP.NET finishes executing all request event handlers. This event causes state modules to save the current state data. When the ReleaseRequestState event is raised, the application is finished with the request and ASP.NET is signaled to store the request state

    HttpModule PostReleaseRequestState Occurs when ASP.NET has completed executing all request event handlers and the request state data has been stored
    HttpModule UpdateRequestCache Occurs when ASP.NET finishes executing an event handler in order to let caching modules store responses that will be used to serve subsequent requests from the cache
    HttpModule PostUpdateRequestCache

    The PostUpdateRequestCache event is raised after the UpdateRequestCache event has occurred. When the PostUpdateRequestCache is raised, ASP.NET has completed processing code and the content of the cache is finalized. Occurs when ASP.NET finishes updating caching modules and storing responses that are used to serve subsequent requests from the cache.

    HttpModule EndRequest

    Occurs as the last event in the HTTP pipeline chain of execution when ASP.NET responds to a request. The EndRequest event is always raised when the CompleteRequest method is called.

    HttpModule PreSendRequestHeaders Occurs just before ASP.NET sends HTTP headers to the client
    HttpModule PreSendRequestContent Occurs just before ASP.NET sends content to the client; The PreSendRequestContent event may occur multiple times
    Session End  
    HttpModule Disposed Adds an event handler to listen to the Disposed event on the application. When you create a Disposed delegate, you identify the method that handles the event. To associate the event with your event handler, add an instance of the Disposed delegate to the event. The event handler is called whenever the event occurs, unless you remove the Disposed delegate.
    Application End Called once per lifetime of the application before the application is unloaded
    WebUserControl Finalize  
    Page Finalize  
    HttpModule Finalize  


    The CreateChildControls method is not listed in the table because it is called whenever the ASP.NET page framework needs to create the controls tree and this method call is not limited to a specific phase in a control's lifecycle. For example, CreateChildControls can be invoked when loading a page, during data binding, or during rendering.


    There is also a quite well-known poster about page lifecycle events, probably you have already seen it (source:

    ASP.NET Page LifeCycle


    I reported it in the table below for easier reading:

    Page ProcessRequest
    Page ProcessRequestMain
    Page DeterminePostBackMode
    Adapter DeterminePostBackMode
    Page LoadScrollPosition
    Page OnPreInit
    Page ApplyMasterPage
    PostBack ApplyMasterPageRecursive
    Control InitRecursive
    Control ResolveAdapter
    Control ApplySkin
    Page ApplyControlSkin
    PostBack ApplyControlSkin
    Page OnInitComplete
    PostBack LoadAllState
    PostBack LoadPageStateFromPersistenceMedium
    PagePersister Load
    Control LoadControlStateInternal
    Control LoadControlState
    Adapter LoadAdapterControlState
    Control LoadViewStateRecursive
    Control LoadViewState
    Adapter LoadAdapterViewState
    PostBack ProcessPostData
    Page OnPreLoad
    Control LoadRecursive
    Control OnLoad
    Adapter OnLoad
    PostBack ProcessPostData
    PostBack RaiseChangeEvents
    PostBack RaisePostBackEvents
    Page OnLoadComplete
    Control PreRenderRecursiveInternal
    Control EnsureChildControls
    Control ResolveAdapter
    Control CreateChildControls
    Adapter CreateChildControls
    Control OnPreRender
    Adapter OnPreRender
    Control SaveViewState
    Page SavePageStateToPersistenceMedium
    PagePersister Save
    Page OnSaveStateComplete
    Control RenderControl
    Control RenderControlInternal
    Control Render
    Adapter BeginRender
    Adapter Render
    Adapter EndRender
    Control RenderChildren

    The solution

    The root cause of this problem was that we were setting the child MultiView control's ActiveViewIndex in the Render stage, which is too late. No "work" should be done in the Render stage, only rendering. There are some exceptions to this, but setting ActiveViewIndex is not one of them. By default the MultiView's ActiveViewIndex is -1, meaning that none of the views are active. The result of this is that all the child View controls are not visible, which means that their OnPreRender methods will not be called. The CheckBox control registers for post back data during OnPreRender but since it's never called, it never registers. Since it's never registered for post back data, it doesn't see the value of the checkbox being posted back.

    We found a few different possible solutions to this problem, so it was up to the customer choose the one they preferred... here are the alternatives:

    • Set the child MultiView's ActiveViewIndex whenever their control's ActiveTabIndex is set. Something like (in MTabView.cs):
    private int ActiveTabIndex_ = -1;
    public int ActiveTabIndex
        get { return this.ActiveTabIndex_; }
            this.ActiveTabIndex_ = value;
            InnerMultiView_.ActiveViewIndex = ActiveTabIndex;
    • Set the child MultiView's ActiveViewIndex in CreateChildControls after the other child controls are created (the table, etc.)
    • Set the child MultiView's ActiveViewIndex in OnPreRender before calling base.OnPreRender().


    Should be easy to image at this point: if you get into a situation like this one, check very carefully where you're doing your changes and the event sequence within your application: very likely your change is applied but overridden later before it get any change to render to the client.



    Quote of the Day:
    There is no worse lie than a truth misunderstood by those who hear it.
    --William James
  • Never doubt thy debugger

    Don't let IE local cache drive you crazy!


    I stumbled across this issue multiple times during my life of web developer (which begun about 10 years ago), it appeared every now and then to complicate things when I was in the middle of a heavy debugging sessions and doing frequent changes to my pages; I was expecting some kind of results but despite the fact that the code was looking good, there were no signs of those changes. Sometimes even adding a new UI element like a button or an image or changing the color of a header had no effect... smile_sarcastic Having a look at the page source within IE demonstrated the browser was somehow right not showing the new image or color because it was not there in the code... where was that source coming from? smile_omg

    Well, if it does not come from the web server, then it's loaded from the IE local cache... so let's go to Internet Options > General > Delete > Temporary Internet Files, give it another try and guess what? This time it works...! So, not sure why, but for some reason IE was not refreshing its case and was using an outdated version of my pages. Then after a while I forgot about problem.

    Last week I got a call from a customer about an applications they are developing, but he was getting a Javascript error on the client-side: "WebForm_PostBackOptions is undefined". Looking at the page source within IE confirmed that the WebForm_PostBackOptions method was really missing, but we had to discover the reason. As you may know, in ASP.NET 2.0 resource files (such as images, CSS stylesheets, javascript etc...) are served through the WebResource.axd handler, which is for example responsible to serve the client-side javascript code used to post back the page, for validation used by the Validation controls etc... There is a known issue with WebResource.axd where using Http Compression prevents the client-side scripts to render correctly and it's necessary to exclude the handler from the compression to have it working normally; so we checked if IIS was using Http compression, but it wasn't.

    Next step has been to use Fiddler to trace the http traffic between the web server and the client and everything looked fine, all resources and files (included WebResource.axd) were downloaded correctly... smile_thinking Then my past experiences with IE temp cache came back to my mind, and asked the customer to clear the IE cache before trying again: well, as you can guess, that solved the problem! For sore reason IE was downloading the .axd file but kept using the cached (corrupted) one, which did not contain the WebForm_PostBackOptions implementation.

    I have to say that every time I encountered this problem, I (or the customer) were developing and debugging on localhost so the machine was somehow playing a double role, not sure it this can be the (very high level) explanation of this weird behavior... anyway remember to clean your local caches and temporary folders if your application starts behaving odd...



    Quote of the day:
    A strong conviction that something must be done is the parent of many bad measures. - Daniel Webster
  • Never doubt thy debugger

    The weight of security


    This morning I had a call with a customer which reported a performance problem opening and compiling a VB.NET 2005 web application made of hundreds of files; we have a couple of hotfixes for VS2005 included in SP1, but the customer already had it.

    He reported a delay in term of dozens of seconds to load, save, compile (doing anything) on the remote project, while opening it on localhost worked like a charm (so it was hard to think that Visual Studio was at fault here…); also copying a big file (120 Mb) from the server to the client just took the time of a click (the network is then working fine)… smile_omg

    To make the story short, I asked him to tell me step by step how he was opening the project: File > Open > Remote Site > http://ipaddress/application. Using the IP address here means the project will be recognized as Internet Zone (IE security) and additional security checks and restrictions will be applied.

    Open web site

    I asked the customer to enter the server name instead of the IP address and everything was fully loaded at the speed of light! To be honest I didn't though that this could have such a huge impact on performance... I guess also the fact that the solution had a lot of small files had an impact, since the security checks involved had to be repeated for every file (while with a big file we can have the same amount of bytes to transfer, but the security is checked just once).




    Quote of the Day:
    Patience is the companion of wisdom.
  • Never doubt thy debugger

    Corrupt installation? Do not repair Visual Studio


    Unable to start debugging on the web server

    (Unable to start debugging on the web server. An error occurred that usually indicates a corrupt installation. If the problem persists, repair Visual Studio installation via 'Add or Remove Programs' in Control Panel)


    I saw this happening on a Vista x64 while trying to debug an ASP.NET application and needless to say (smile_sad), repairing Visual Studio does not help.

    This is a misleading error message which might appear when you try to debug an ASP.NET application on a 64 bit OS and you configured your application pool to run a 32 bit worker process; I know it will be changed to a more meaningful message, but I'm not sure about the timeframe (I can't repro so I'm not able to check how Visual Studio 2008 behaves).

    What to do then? Check the advanced settings for your application pool and set "Enable 32-bit applications" to "False"

    Advanced settings 

    By the way, I was this error in conjunction with this one so pay attention if you're hitting one of the two...



    Quote of the Day:
    Sincerity is the highest compliment you can pay.
    --Ralph Waldo Emerson

Page 1 of 1 (4 items)