(If you don’t get the title reference, Wikipedia can explain. A more direct title could be: Understanding and Respecting the ASP.NET Page Lifecycle.)
This whole article needs a technical review. Parts of it are misleading. I’ll get back to you Barry.
Page lifecycle in ASP.NET is a finicky and rarely understood beast. Unfortunately, it’s something that we all need to get a handle on.
A common mishap that I see is code like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
AddressCountryDropDown.DataSource = CountriesList;
AddressCountryDropDown.DataBind();
}
}
The problem here is that we’re clogging our page’s view state. Think of view state as one of a page’s core arteries, then think of data like cholesterol. A little bit is all right, but too much is crippling.
To understand the problem, lets investigate the lifecycle that’s in play here:
The Page.Init event is being fired, however we are not subscribed to that.Immediately after the Init event has fired, view state starts tracking. This means that any changes me make from now on will be saved down to the browser and re-uploaded on the next post back.The Page.Load event is being fired in which we are setting the contents of the drop down list. Because we are doing this after the view state has started tracking, every single entry in the drop down is being written to both the HTML and the view state.
There’s yet another problem here as well. By the time the Page.Load event is fired, all of the post back data has been loaded and processed.
To investigate the second problem, let’s investigate the lifecycle that’s in play during a post back of this same page:
The user triggers the post back from their browser and all of the post back data and view state is uploaded to the server.The Page.Init event is fired, however we are not subscribed to that.Immediately after the Init event has fired, view state starts tracking. This means that any changes me make from now on will be saved down to the browser and re-uploaded on the next post back.The view state data is loaded for all controls. For our drop down list example, this means the Items collection is refilled using the view state that was uploaded from the browser.Post back data is processed. In our example, this means the selected item is set on the drop down list.The Page.Load event is fired however nothing happens because the developer is checking the Page.IsPostBack property. Usually, they say this is a “performance improvement” however it is also required in this scenario otherwise we would lose the selected item when we rebound the list.The contents of the drop down list are once again written to both the HTML and the view state.
How do we do this better? Removing the IsPostBack check and placing the binding code into the Init event is all we need to do:
protected override void OnInit(EventArgs e)
{
AddressCountryDropDown.DataSource = CountriesList;
AddressCountryDropDown.DataBind();
base.OnInit(e);
}
What does this achieve?
We are filling the contents of the drop down before the Init event is fired; therefore a redundant copy of its contents is not written to the view state.We are filling the contents of the drop down before the postback data is processed, so our item selection is successfully loaded without it being overridden later.We have significantly reduced the size of the page’s view state.
This simple change is something that all ASP.NET developers need to be aware of. Unfortunately so many developers jumped in and wrote their first ASP.NET page using the Page_Load event (including myself). I think this is largely because it’s the one and only event handler presented to us when we create a new ASPX page in Visual Studio. While this makes the platform appear to work straight away, it produces appalling results.
Hi Tatham, i’m not sure I can completely agree with this approach. What we don’t know here is the impact of retrieving the CountriesList. If this is expensive, performance wise, then it would be preferable to do this once in the first page load and then maintain it in ViewState.
Also, if the amount of data being stored in ViewState is of concern then each control can be flagged to disable the storing of data in ViewState using the EnableViewState property. This can also be set at page level using a Page Directive.
Hi Andy,
Thanks for your response.
If the list of countries is expensive to retreive, although common across different users, then we should be storing it on the servers cache. This way we can keep our view state small as well as improving performance for _all_ users on the site.
Re: EnableViewState. If we populate the control at the correct stage of the lifecycle, we do not need to fallback to setting this property. Filling a control from the Load event, wrapping it in an IsPostBack check and setting EnableViewState to false sounds like a lot more hacking than just moving the population code to the Init event.
– Tatham
Should the code go into the Control’s init handler rather than the page’s? That might provide for better separation.
Hi Josh,
In this case I was using a standard DropDownList control as an example.
I discuss separating things into custom controls in my next post:
http://blog.tatham.oddie.com.au/2008/12/20/accessing-aspnet-page-controls-during-preinit/
I broke it out separately because:
a) not everything can be subclassed (or is worth it)
b) to subclass it, you need to be aware of an intricacy with the PreInit event which I felt went beyond the scope of this post.
Thanks for the great comment!
– Tatham
Although I agree that an asp.net developer has to keep an eye on the viewstate the pattern you describe of binding the result of an expensive call to the database in the pageload event is actualy intended. I don’t want to speak for the architects that made asp.net but their intention was that to make scalable web applications the client got involved in maintaining state.
If you aim for high performant websites that scale it is actualy beneficial to store results on the client (within reason!) so that the server can get all the info it needs of the browser request and doesn’t need to query the database in successive calls.
If viewstate becomes too big than your have the solution in your post if you put the database result in the cache. But if the information is to be scope to the user and not the whole application it *can* become less scalable if the info was stored in a session variale on the server (1000 users = 1000 * size of the session).
After reading a few books on wcf and conurrent systems I learned that the server is busy with only a small percentage of its users at any give time (I dont know the exact and it depents on the situation but the percentage but its surprisingly low).
If the server can “forget” everything after a request can be handled its througput can be increases. Using the client as a database (viewstate) makes it possible for the server to handle each next request “in memory” and he does not have to remember a thing for the client.
Hi Tom,
Thanks for your comment!
I definitely agree with you that the applications with the best opportunity to scale are those that do not require server-side statefulness. View state is a great tool for involving the client in this process.
That being said, loading a list of countries (which was my example) is by no means an expansive exercise. It also places no requirement on server side state if we are to remove this from the view state – it’s the same list of countries irrespective of who is loading the page.
If it was an expensive list to load, we could cache this in the HttpContext.Current.Cache property which is shared between sessions and thus would not affect our capacity to scale.
If we needed to filter the list of countries in a way that was session-specific, such as only showing countries within a specific region, all we need to do is store the region in the view state. The process of evaluating which countries are in that region and loading them into the control can still be completed in the Init event. This keeps our view state small and scalability high at the same time.
Do you agree with all that?
– Tatham
hi tatham,
thank u for this article…
however when i tried placing my code to populate a treeview inside the page init event the items added were duplicated for each page postback.
can you pls. explain why this is happening and what is the proper way to fix it?
thanks.
hi tatham
i tried loading the treeview data only when it isn’t a page postback and the duplication was fixed
is this the right way to do it?
Hi Tatham – I am really impressed with your articles on various .NET related topics. Thanks!
By the way, What is your recommendation on the above scenario when the Dropdownlist is bound by a “ObjectDataSource” control that does the real databinding only during “PreRenderComplete” event which is way late in the page lifecycle game & I believe ViewStateTracking would happen in this case…
Appreciate your response!
Thanks
Chandra