I spent the last days mainly on the ViewState. Read a lot about it and tried even more things. Quite interesting, often very frustrating, but worth a roundup which leads to a positive conclusion. Lets' start with a small recap. The viewstate of an asp.net webpage is the vehicle in which the state of a web page travels. When the user posts a page back to the server the server will return a new copy of the webpage in which the state of all controls, like the text in a textbox or the contents of a datagrid, is preserved. This state is encoded in a hidden form field on the page named __ViewState, you will see it when you do a view source in your browser. This field is included in the response coming from the server but also in the request posted back to the server. With the state (-changes) stored in the html page the webbrowser plays an important role in state mangement.
Almost every property (-change) of a webcontrol is stored in this viewstate. One of my apps has pages with many row datalists, the size of the request alone sometimes exceeded 100 K ! Which resulted in a not so snappy performance and was the main reason for my investigations. My first attempt was to completely get rid of the viewstate. The System.Web.UI.Page has two methods which deal with its persistence. Both are virtual an can be overridden.
protected override void SavePageStateToPersistenceMedium(object vs)
{
base.SavePageStateToPersistenceMedium(vs);
}
protected override object LoadPageStateFromPersistenceMedium()
{
return base.LoadPageStateFromPersistenceMedium();
}
The default base implementation of the first method stores the serialized viewstate in the hidden __viewstate field on the page and the second retrieves the viewstate from the page. On the web I found several people who had overridden this method to store the viewstate in the session instead of the page. The viewstate data then stays on the server. Some solutions were pretty straightforward, they used one session variable to store every viewstate in the session. Which could work as long as the session has only one page open. Another implementation recycled an array of multiple states and another one even maintained a dynamic collection of sessions vars, one for every url. Especially the latter will lead to gigantic scaling problems. Just imagine a user having an all day session visiting a couple of hundreds of pages. It only takes a couple of those to cripple any web server. I am not giving you any links to these sources, besides their theoretical issues they just do not work. I also use the viewstate in my code to store some of my own stuff. Maybe that is the reason, maybe something else, but I experienced that every viewstate given back by the session was screwed up.
Another solution to get rid of the viewstate hog is to disable it. Every control (including a page) has an EnableViewState property. Default this is set to true. The most pages of my application contain a DataList component with database data. This dnj article describes the idea. In the article and its precursor on the DataGrid I already mentioned some thoughts on the viewstate. As the data in the list are re-read on every roundtrip they do not have to be persisted in the viewstate. The bad thing is that setting the list's EnableViewState property to false screws up the working of the EditItemIndex and SelectedItemIndex property. You can try to handle those properties in your own code. The DataGrid and the DataList components have an SelectedIndexChanged event which will get fired on an index change and they do receive the correct new index in the event's arguments. But the bad thing is that these events get fired too late. In my article was an overview of the events on a page roundtrip, the problem is that the components only render as desired when the index is set in the initialization events, like Init. The SelectedIndexChanged event fires later, setting the SelectedIndex property in there will result in a correct rendering of your component on the next roundtrip. Not really a desired behaviour. The EnableViewState property of a DataList or DataGrid just has to be enabled for the controls to be usable.
The good thing with the EnableViewState property is that it can be disabled on almost all other controls. The data is presented in (data bound) text-boxes and labels in the templates of the list and is reloaded in the DataBind of every roundtrip, so they do not need the viewstate. For linkbuttons and normal buttons it is almost ridiculous to have a viewstate, except when you want their appearance to change over roundtrips. The only controls which really do make a sensible use of the viewstate are (dropdown-)listboxes and treeviews. If you disable their viewstate you will not be able to see which item was selected by the user. It would make sense to get the contents of the list out of the viewstate as that will not often change over roundtrips, more on that later. After a lot of property editing and testing I was disable the viewstate for most of the components. The result was great. I tested the viewstate size by looking at the size of the request :
Response.Write(string.Format("
RequestSize {0} Bytes
", Request.TotalBytes));
On some pages it resulted in a reduction of 90% in the size of the request and 50% in the size of the response. Without the loss of any functionality. The next question is how the app will behave on the intranet of my customer.
This ViewState matter has also been adressed in Whidbey. In there a control has besides its viewstate a controlstate property. The controlstate is used to persist those properties which are essential to a component. The controlstate cannot be disabled. The ViewState is still there as well. But after all, the only real problem is that it is enabled by default.
Blog on,
Peter