WebForms/Page life cycle

From eBabel wiki
Jump to: navigation, search

Web Forms page cycle

On the server, each request trigers a series of stages that follow a fixed order.

  1. End user requests a page
  2. Page framework initialisation
  3. User code initialisation
  4. Validation
  5. Event handling
  6. Response sent to the end user
  7. Cleanup

Page framework initialisation

  • ASP.Net creates the page.
  • All the controls from the aspx view are generated (code behind isn't run yet).
  • If this is a postback, the view state is deserialised and applied to the controls that have just been generated.
  • Page.Init is run (handled by Page_Init method in code-behind). Page.Init is actually from Control.Init in System.Web.UI.Control
Warning
When Page.Init runs, the controls haven't necessarily been fully generated and the view state hasn't necessarily finished loading from the client.

User code initialisation

  • Page.Load is run every time the page is processed, whether the page loads for the first time or is requested again as part of a PostBack request (handled by Page_Load method in code-behind).
Warning
If the data has to be populated dynamically from code-behind, make sure that a PostBack doesn't connect to the database again to request that data and repopulates the control. 
Use IsPostBack in the Page.Load method to make sure the database isn't hit more than necessary.
Note
When there is a PostBack, Page.Init doesn't seem to run again. 
However, before running Page.Load, there are still steps from the Page framework initialisation that are run, like creating the page, the controls and deserialising the view state.

Validation

  • The controls are checked for validation rules. No extra code should be written there. The only property you might want to access is Page.IsValid, but the validation mechanism and the rendering of error messages should be delegated to the controls and the .Net framework, it would be bad practice to add validation code in code-behind for a specific page.
  • The Validation events are triggered before any other events in the page life cycle.

Event handling

  • Page is now fully loaded and validated
  • Two types of events are not handled:
    • Click events that call the __DoPostBack() Javascript code generated by controls.
    • Change events that are triggered by modifying the state of controls, like selecting a different option in a select menu or changing the content of a text box.
Note: for Change events, they can be triggered immediately if the AutoPostBack of that control is set to True.

Response sent to the end user

The completed client side code (HTML, CSS and Javascript) is now sent to the end user. No more changes can be made from the server side to that output as part of this page life cycle.

Cleanup

All controls should be marked for disposal at this stage. It's true the garbage collector will eventually mark for disposal and dispose of unused resources, but it's best to explicitly mark the resources you know are no longer required, because the garbage collector only works when memory allows, to optimize performance. It's therefore good practice to help the garbage collector by explicitly marking unused resources for disposal with the .Dispose() method in the Cleanup phase of the page life cycle.

Marking these resource for disposal doesn't mean they are disposed right away. It's still the garbage collector that will dispose of them in due time.

Note: the garbage collector has no problems with disposing of small resources. Resources that are larger in size and use up more memory, however, are harder for the garbage collector to handle. 
ToDo: I don't have a solution yet on how to handle these large objects and exactly how large is too large for the garbage collector.

How to optimise view state

Web form before view state optimisation

In the simple example below where a single ListBox control has only a few items, it's enough to submit to submit it a few times to see the view state grow accordingly. That's because every time the control is changed by having its data populated in code-behind, these changes are tracked in view state and all the data is kept there.

View ThePageCycle.aspx

<syntaxhighlight lang="xml" line highlight="8,10">

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ThePageCycle.aspx.cs" Inherits="PageCycle1.ThePageCycle" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
   

Running order of the page cycle

   <asp:ListBox runat="server" ID="Info" SelectionMode="Multiple" />
   

<asp:Button ID="SendPostBack" runat="server" Text="Send PostBack" />

</asp:Content>

</syntaxhighlight>

Code-behind ThePageCycle.aspx.cs

<syntaxhighlight lang="csharp" line>

using System;
using System.Web.UI;

namespace PageCycle1
{
   public partial class ThePageCycle : Page
   {
       protected void Page_Init(object sender, EventArgs e)
       {
           Info.Items.Add("1: Page.Init");
       }

       protected void Page_Load(object sender, EventArgs e)
       {
           Info.Items.Add("2: Page.Load");

           if (IsPostBack)
           {
               // This line is added if the form is submitted, which will re-process the page.
               Info.Items.Add("2b: Page.Load with PostBack");
           }
       }

       protected void Page_PreRender(object sender, EventArgs e)
       {
           Info.Items.Add("3: Page.PreRender");
       }

       protected void Page_Unload(object sender, EventArgs e)
       {
           // Info still exists but the page HTML has been rendered and already sent to the end user, so you won't see "4: Page.Unload" in the ListBox control.
           Info.Items.Add("4: Page.Unload");

           // Garbarge collection shouldn't be relied on to decide which controls to clear when it gets a chance to run. 
           // It's best practice to explicitly mark objects for garbage collection so  that memory is freed more reliably.
           // All the controls of a page are not needed once the Page life cycle hits the Unload stage.
           Info.Dispose();
       }
   }
}

</syntaxhighlight>

Two ways to optimise view state

There are two ways to prevent view state from growing with each web form postback.

No view state solution

The first would be to scrap the tracking entirely and to disable view state on the control. This is done by setting EnableViewState to False in the control tag:

<syntaxhighlight lang="xml" line start="8">

<asp:ListBox runat="server" ID="Info" EnableViewState="False" SelectionMode="Multiple" />

</syntaxhighlight>

However that means that changes previously made dynamically via code-behind in the control won't be tracked at all. The content of the control will only get values from the latest processing of code behind, not previous ones since they haven't been stored in view state.

This may or may not be acceptable depending on how the control is used.

Reduced view state solution

A second way to prevent view state from growing, is to keep it enabled but to populate the content of the ListBox control with tags in the view instead of using code-behind.

Of course the functionality isn't exactly the same, the content is not augmented dynamically. However the user selection in the ListBox control are preserved via view state. All controls that can work this way should be coded so that they don't get populated dynamically from code-behind.

View OptimisedViewState.aspx

<syntaxhighlight lang="xml" line highlight="9,10,11,12,13">

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="OptimisedViewState.aspx.cs" Inherits="PageCycle1.OptimisedViewState" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    

Optimised View State

   <asp:ListBox runat="server" ID="Info" SelectionMode="Multiple">
       <Items>
           <asp:ListItem Text="1: Page.Init" Value="1" />
           <asp:ListItem Text="2: Page.Load" Value="2" />
           <asp:ListItem Text="3: Page.PreRender" Value="3" />
       </Items>
   </asp:ListBox>
    

<asp:Button ID="SendPostBack" runat="server" Text="Send PostBack" />

</asp:Content>

</syntaxhighlight>

Code behind OptimisedViewState.aspx.cs

<syntaxhighlight lang="csharp" line>

using System;
using System.Web.UI;

namespace PageCycle1
{
   public partial class OptimisedViewState : Page
   {
       protected void Page_Init(object sender, EventArgs e)
       {
       }

       protected void Page_Load(object sender, EventArgs e)
       {
       }

       protected void Page_PreRender(object sender, EventArgs e)
       {
           
       }

       protected void Page_Unload(object sender, EventArgs e)
       {
       }
   }
}

</syntaxhighlight>