Viewstate of Mind

ASP.NET Viewstate Is Critical to Minimize the End-to-end Response Time of Web Pages

Dino Esposito

October 30, 2009

11 Min Read
ITPro Today logo in a gray background | ITPro Today

CoreCoder

LANGUAGES: C#| VB.NET

ASP.NETVERSIONS: 1.x | 2.0

 

Viewstate of Mind

ASP.NET Viewstate Is Critical to Minimize the End-to-endResponse Time of Web Pages

 

By Dino Esposito

 

It s widely known that most ASP.NET data-bound controlsdon t persist their data source to the viewstate across postbacks. The reasonis easy to guess: performance. Any sort of information stored to the viewstatefinds its way to a hidden field, gets serialized, Base64-encoded, and, finally,downloaded to the client.

 

Is that all? Well, no there s more. You pay the price ofthe viewstate extra burden twice. What do you think happens when the usersubmits the page back to the Web server? The whole form contents are posted tothe server, including the viewstate hidden field. You pay the price ofviewstate extra data in both download and upload. To keep the viewstate sizereasonable, most data-bound controls do not persist their data source, which islikely to be a pretty large chunk of data.

 

For this reason, an accepted practice suggests that youinsulate your data initialization code into the Page_Load event. In particular,you place it in the false branch of an IF statement guarded by the value of thepage s IsPostBack property. Is this enough to avoid that a large chunk of dataflows into the viewstate? Definitely not.

 

List Controls Surprise

Let s consider a classic example. Imagine your pageincludes a dropdown list control filled with country names (a very popular formfeature indeed). I can t really say how many countries there are in the worldthis week, but the order of magnitude of the actual number is certainly that ofhundreds. Let s assume you have a few hundred names. Here s a possible way tofill the dropdown list:

 

(C#)

if (!IsPostBack)

{

 DataTable data = new DataTable();

 data.Columns.Add("Country", typeof(string));

 for(int i=0; i<500;i++) {

   DataRow row =data.NewRow();

   row["country"] = "Country #" + i.ToString();

   data.Rows.Add(row);

 }

 countries.DataSource =data;

 countries.DataTextField ="Country";

 countries.DataBind();

}

 

(VB.NET)

If Not IsPostBack Then

 Dim data As New DataTable

 data.Columns.Add("Country", GetType(String))

 For i As Integer = 0 To500

   Dim row As DataRow =data.NewRow()

   row("country") = "Country #" + i.ToString()

   data.Rows.Add(row)

 Next

 countries.DataSource =data

 countries.DataTextField ="Country"

 countries.DataBind()

End If

 

Run the page with the tracing option turned on and take alook at the results. Figure 1 shows what happens. What s the circled number?Just the size in bytes of a portion (I repeat, portion) of your page s viewstate. That s right, you have more than17 KB of viewstate in an otherwise straightforward page filled with only twocontrols.

 


Figure 1: A dropdown list controlserializes the contents of its Items collection to the viewstate.

 

What s up? Simply put, the whole list of countries ishappily persisted in the viewstate. But wait; we did say that most data-boundcontrols don t deliberately save their data source to the viewstate, didn t we?

 

The data source is not persisted, per se; however, when DataBind is called on a list control, thecontents of the data source are loaded into the Items collection and used forrendering. The rub lies in the implementation of the Items collection (somethingsimilar happens with DataGrid controls, also).

 

In ASP.NET 1.1, there are two non-exclusive ways to feedlist controls: through the Items or DataSource properties. Internally, thecontrol loads everything into the Items collection and reads from there when it sabout time to render out. The Items property is an instance of theListItemCollection type. As you can see in the documentation, this typeimplements the IStateManager interface. The interface defines the propertiesand methods any class must implement to support viewstate management for aserver control. Quite a few classes implement that interface in the ASP.NETframework, the most important of which are DataGridColumnCollection, Style,and, as mentioned, ListItemCollection. Anything you store in instances of theseclasses is persisted to the viewstate.

 

What can you do to work around this issue and have yourviewstate undergo a dramatic slimming diet? Although the example discussedunveils an unsuspected misconception, the problem is just a new instance of aknown issue; to solve it you must look at one of two known approaches:

  • Leave the viewstate on the server;

  • Disable the viewstate for some controls.

 

From the client s perspective, the effect is nearly thesame. If you leave the viewstate on the server, no hidden field will ever bedownloaded to the client and uploaded back during postbacks. If you disable theviewstate for all or some controls, you dramatically reduce the size of thefield up to an absolute minimum of 20 bytes.

 

However, the server-side implications of both approachesare different. Different considerations apply to both techniques and make eachsuitable for different scenarios. In this article, I ll take a look at somepractical implications of programming with a disabled viewstate.

 

Programming with a Disabled Viewstate

You can disable the viewstate on individual controls orall controls located in the page. To turn off viewstate on controls, youtypically set the EnableViewState property to false. To turn off viewstate atthe page level, you can use the EnableViewState attribute of the @Pagedirective.

 

Disabling the viewstate for the dropdown list control ofthe previous example results in a significant cut of the viewstate burden. Itdoesn t come free of issues, though. As Figure 2 shows, disabling the viewstatefor the dropdown list control generates a much slimmer page. Note that the sizeof the viewstate never goes below 20 bytes, because that is just the size ofthe key added to protect the viewstate against tampering with.

 


Figure 2: The viewstate of thedropdown list control has been disabled.

 

It goes without saying that dropping the viewstate of asingle control rarely brings the total size down to just a few bytes in areal-world page.

 

What are the drawbacks of this solution? The viewstateplays a fundamental role in ASP.NET pages because it takes a snapshot of thepage and control s state and restores it once the page is posted back to theserver. The viewstate is key to emulating a stateful model on top of astateless protocol like HTTP. The viewstate is also indissolubly tied to aparticular instance of the page and becomes part of its context. It is not usedon the client, but travels with the page request. All in all, I believe thatthe actual implementation of the viewstate to be the best possible defaultapproach because it balances out various factors: load on the client, speed ofrestoration, server memory, and code maintenance, readability, and flexibility.You must be aware, however, that there might be situations where the defaultapproach just doesn t work all that well. Let s drill down into the countriesdropdown list control.

 

Reasonably, you fill the control with the results of adatabase query or, perhaps, with contents read out of a text file. You bind thesource to the dropdown list in the Page_Load event when IsPostBack is false.Well, in this case, the page works just fine as long as the viewstate isturned on. If not, the first time the page posts back the dropdown list controllooks empty. But why?

 

Viewstate-effective Data Binding

With the viewstate feature turned off, there s noinformation to recreate the control s state as it was the last time thatinstance of the page was served. On the other hand, data binding takes place onlythe first time the page is loaded (when IsPostBack returns false). As you cansee, there s no code path that ultimately refills the dropdown list. This isthe main drawback of disabling the viewstate.

 

A page viewstate of just a few KB is acceptable. Forexample, a viewstate size of about 3 KB is optimal. For pure curiosity, Ichecked the size of the viewstate of some well-known pages. The home page ofthe MSDN site is less than 2 KB. The viewstate grows up to 90 KB in the controlgallery page on http://www.asp.net. In thiscase, the page is a long list of descriptions and links, presumably createdusing a Repeater or a similar control.

 

What should you do when you have a viewstate that is too large?It depends on the characteristics of the data that crowds the field. Keep anydata that really represents state information that can t be (easily)recalculated; drop any data that is constant, static, or that can be obtainedwith limited efforts in terms of computation and memory occupation. The list ofworld countries certainly belongs to the second category. You can read the dataonce and put it into the ASP.NET cache. Upon postback, you retrieve a referenceto the data from the cache and bind it to the control. Figure 3 shows how to dothat.

 

<%@ Page Language="C#" Trace="true" %>

<%@ Import Namespace="System.Data" %>

private void Page_Load(object sender, System.EventArgs e) { if (!IsPostBack)  {    LoadData(); }  BindCountryList();}private void LoadData(){ DataTable data = newDataTable(); data.Columns.Add("Country",typeof(string));      for(int i=0; i<500;i++) {    DataRow row =data.NewRow();   row["country"]= "Country #" + i.ToString();   data.Rows.Add(row);  }  Cache["MyData"]= data; }private void BindCountryList(){ object o =Cache["MyData"];  if (o == null)  {    LoadData();   o =Cache["MyData"];  }    DataTable data =(DataTable) o;  countries.DataSource =data;  countries.DataTextField ="Country";  countries.DataBind();  }

   "countries"enableviewstate="false" />  

Figure 3A: Viewstate-effectivecountry list control (C#).

 

<%@ Page Language="VB" Trace="true" %>

<%@ Import Namespace="System.Data" %>

Private Sub Page_Load(ByVal sender As Object,                      ByVal e As System.EventArgs)  If Not IsPostBack Then   LoadData() End If BindCountryList()End SubPrivate Sub LoadData() Dim data As New DataTable data.Columns.Add("Country",GetType(String))      For i As Integer = 0 To500   Dim row As DataRow =data.NewRow()   row("country")= "Country #" + i.ToString()   data.Rows.Add(row)  Next Cache("MyData")= dataEnd SubPrivate Sub BindCountryList() Dim o As Object =Cache("MyData")  If Not (o Is Nothing) Then   LoadData()   o =Cache("MyData")           End If   Dim data As DataTable =CType(o, DataTable)  countries.DataSource =data countries.DataTextField ="Country"  countries.DataBind()End Sub

   "countries"enableviewstate="false" />  

Figure 3B: Viewstate-effectivecountry list control (VB.NET).

 

If you run the code shown in Figure 3, you experience anextremely slim and fast viewstate and no bad surprises when you post back.Really? Try selecting any item in the dropdown list different from the firstand then post back. As you may have imagined, the index of the currentlyselected country is lost in the postback. Again, this is another side effect ofdisabling the viewstate.

 

You might think that the selected index of an indexshouldn t go to the viewstate; moreover, it should be perhaps the only propertyout of a control to work fine regardless of the viewstate. As you can see, thisis not the case, because SelectedIndex is implemented through the Selectedproperty of the Items collection. In other words, it is indirectly persisted tothe viewstate and, in case of a disabled viewstate, it is not automaticallyresolved through the IPostBackDataHandler interface. You must take a differentroute to solve the issue.

 

The simplest workaround consists of binding in Page_Initrather than Page_Load. In this case, the Items collection is not empty when themethods of IPostBackDataHandler are called and the value posted is correctlyapplied.

 

Separating Private and Public Viewstate

Following is what I suggest if you don t want to move codeout of Page_Load (which is my preference). Web programming was possible foryears before the advent of ASP.NET and the viewstate. How would you solve thisin ASP, for instance? I guess you would have used the old faithful Requestobject. Why not here, then? The Request object collects input parametersextracted from the raw HTTP packet. The packet contains client-side values, whichis the text of a textbox and the selected index of a dropdown list. Add thefollowing code at the bottom of the BindCountryList method in Figure 3:

 

(C#)

if (IsPostBack)

{

 string item =Page.Request["countries"].ToString();

 ListItem tmp = newListItem(item);

 int index =countries.Items.IndexOf(tmp);

 countries.SelectedIndex =index;

}

 

(VB.NET)

If IsPostBack Then

 Dim item As String = Page.Request("countries").ToString()

 Dim tmp As ListItem = NewListItem(item)

 Dim index As Integer =countries.Items.IndexOf(tmp)

 countries.SelectedIndex =index

End If

 

Pay attention to add the code right after the call toDataBind. The code snippet retrieves the text of the selected item, finds theindex of the associated ListItem object, and simply selects that index in thedropdown control. In the end, you ll have a page that works as expected but is17 KB slimmer. Be honest, it s not a bad deal!

 

Dino Esposito is aWintellect trainer and consultant who specializes in ASP.NET and ADO.NET.Author of Programming Microsoft ASP.NETand Introducing ASP.NET 2.0, both fromMicrosoft Press, Dino also helped several companies architect and buildeffective products for ASP.NET developers. Dino is the cofounder of http://www.DotNet2TheMax.com, apopular portal for .NET programmers. Write to him at mailto:[email protected] or jointhe blog at http://weblogs.asp.net/despos.

 

Can t Wait for Control State to Arrive

As I explained in my October 2004 asp.netPRO article, Maintain State Control, the state of acontrol is logically split into two parts: private and public. In terms of thephysical implementation in ASP.NET 1.1, though, the state is a monolithic chunkof data that can be atomically disabled or enabled. ASP.NET 2.0 introduces aneat separation between the two, and my previous article provides some guidanceon how to accomplish that in ASP.NET 1.1, as well. With control state, eachcontrol can maintain a sort of private viewstate that is not affected bypage-level viewstate settings. This private viewstate, on the other hand,should be limited to storing only those properties for which state isessential. Normally, appearance properties don t need to go to the viewstateunless they can change dynamically to reflect, say, a particular state of thecontrol not otherwise computable.

 

 

 

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like