Determining an ASP.NET Page's View State Footprint

Published 06 July 10 11:35 PM | Scott Mitchell

ASP.NET view state is the technique used by an ASP.NET Web page to persist changes to the state of a Web Form across postbacks. The view state of a page is, by default, placed in a hidden form field named __VIEWSTATE, and can easily get very large. Not only does the __VIEWSTATE form field cause slower downloads, but, whenever the user posts back the Web page, the contents of this hidden form field must be posted back in the HTTP request, thereby lengthening the request time, as well. Because view state is buried in a hidden form field and because as developer we typically test locally, the affect of view state bloat on the user experience requires preemptive action. Steps must be taken to measure view state's size and to take steps in trimming down view state if it begins to negatively affect the user experience.

There are a variety of techniques for measuring the view state size on a given page. Perhaps the simplest is to turn on page tracing. With page tracing enabled you can see the estimated view state of each control in the control hierarchy. For instance, the screen show below shows that two Labels on my page consume (roughly) 120 bytes of view state each.

There's also a free add-on for Firefox that reports view state size directly in your browser window: Viewstate Size. When installed, this add-on will show a little icon in your browser's lower right corner reporting the view state size for a given page.

 

It's also possible to determine the view state size programmatically. Today I was working on a demo for an article and wanted to have the view state size displayed on every page. After reading an article I wrote back in 2004 - Understanding ASP.NET View State - I came up with the following code snippet to compute and display the view state size for a given page. This technique uses the "old school" way of doing it, relying on objects that have been around since ASP.NET's inception. Consequently, the below code will work in any ASP.NET application.

Visual Basic Version:

Protected Overrides Sub SavePageStateToPersistenceMedium(ByVal state As Object)
    MyBase.SavePageStateToPersistenceMedium(state)

    Dim formatter As New LosFormatter()
    Dim viewStateLength = -1

    Using writer As New StringWriter()
        formatter.Serialize(writer, state)
        viewStateLength = writer.ToString().Length
    End Using

    Response.Write(String.Format("<h1 class="" viewstate-summary="">Total ViewState Size For This Page: {0:N0} bytes</h1>", viewStateLength))
End Sub

C# Version:

protected override void SavePageStateToPersistenceMedium(object state)
{
    base.SavePageStateToPersistenceMedium(state);

    var formatter = new LosFormatter();
    var viewStateLength = -1;

    using (var writer = new StringWriter())
    {
        formatter.Serialize(writer, state);
        viewStateLength = writer.ToString().Length;
    }

    Response.Write(String.Format(@"<h1 class="" viewstate-summary="">Total ViewState Size For This Page: {0:N0} bytes</h1>", viewStateLength));
}

The approach overrides the SavePageStateToPersistenceMedium method, which is what is invoked when the page is ready to persist its view state. In ASP.NET 1.x you would override this method to store view state to an alternative store, such as to a file on the web server, rather than in a hidden input field on the form. Starting with ASP.NET 2.0 you could use the PageStatePersister class. However, here I just want to compute the size of the serialized view state string. Consequently, I start by calling the base class's SavePageStateToPersistenceMedium method. Next, I replicate the default logic that the ASP.NET Page class uses to serialize view state - namely, I create a LosFormatter object and serialize the content to a StringWriter. Once I've done this I can determine the size of the persisted view state by getting the StringWriter's contents and determining how many characters are included. The above code can be placed directly in a code-behind class or, better yet, in a custom base Page class.

The above programmatic approach is relatively easy to implement and works in all versions of ASP.NET. However, it won't produce the precise view state size for ASP.NET 2.0 applications and beyond. This is because the LosFormatter class was replaced with a new formatting type, ObjectStateFormatter, starting in ASP.NET 2.0. (You can certainly use the LosFormatter code above in ASP.NET 2.0 and beyond applications, as the class remains in the framework for backwards compatibility. Granted, the reported view state size and actual size will differ slightly, but in analyzing whether a page contains too much view state or not, precise numbers aren't required - a close estimate will usually suffice.)

To get precise measurements we can use the ASP.NET 2.0+ by doing the following:

  1. Create a class that extends the HiddenFieldPageStatePersister class. This is the class that is used by ASP.NET (by default) to persist view state and other information to hidden form fields. We want to extend this class to include a property that reports the size of the persisted state, and
  2. Override the Page class's PageStatePersister property and return the custom persister class created in step 1.
  3. Override the Page class's SavePageStateToPersistenceMedium method and read the value of the view state size from the class created in step 1.

Here's the code for the class that extends HiddenFieldPageStatePersister, which I've named MyHiddenFieldPageStatePersister (please don't mock me for my lack of naming creativity). This class is pretty simple: it defines a property named StateSize, overrides the Save method, and then sets the StateSize property from this method using the same code/logic as used internally by the HiddenFieldPageStatePersister class. The code snippet below takes advantage of some language features that you might not have at your disposal if you're using an older version of C# or VB (for example, the use of automatic properties). But the concept is sound and should work (with a little tweaking) in earlier versions of VB/C#. (I tested this using ASP.NET 4. You can download the complete source code here.)

Visual Basic Version:

Public Class MyHiddenFieldPageStatePersister
    Inherits HiddenFieldPageStatePersister

    Public Sub New(ByVal page As Page)
        MyBase.New(page)
    End Sub

    Public Overrides Sub Save()
        If MyBase.ViewState IsNot Nothing OrElse MyBase.ControlState IsNot Nothing Then
            Me.StateSize = MyBase.StateFormatter.Serialize(New Pair(MyBase.ViewState, MyBase.ControlState)).Length
        End If
        
        MyBase.Save()
    End Sub

    Public Property StateSize As Integer
End Class

C# Version:

public class MyHiddenFieldPageStatePersisterCS : HiddenFieldPageStatePersister
{
    public MyHiddenFieldPageStatePersisterCS(Page page) : base(page) { }

    public override void Save()
    {
        if (base.ViewState != null || base.ControlState != null)
            this.StateSize = base.StateFormatter.Serialize(new Pair(base.ViewState, base.ControlState)).Length;

        base.Save();
    }

    public int StateSize
    {
        get;
        private set;
    }
}

The last step, then, is to override the Page class's PageStatePersister property and return an instance of our persister class, MyHiddenFieldPageStatePersister. Again, this can be done in the code-behind class of an ASP.NET page, but I recommend putting it in a custom base Page class.

Visual Basic Version:

Private _PageStatePersister As PageStatePersister = Nothing

Protected Overrides ReadOnly Property PageStatePersister As System.Web.UI.PageStatePersister
    Get
        If _PageStatePersister Is Nothing Then
            _PageStatePersister = New MyHiddenFieldPageStatePersister(Me)
        End If

        Return _PageStatePersister
    End Get
End Property

C# Version:

private PageStatePersister _PageStatePersister = null;

protected override PageStatePersister PageStatePersister
{
    get
    {
        if (_PageStatePersister == null)
            _PageStatePersister = new MyHiddenFieldPageStatePersisterCS(this);

        return _PageStatePersister;
    }
}

EDIT [2010-07-07]: I forget an important final piece of the puzzle for using the MyHiddenFieldPageStatePersister class! In addition to overriding the PageStatePersister property you also need to override the SavePageStateToPersistenceMedium method like in the LosFormatter example, but instead of using LosFormatter you need to read the value of the StateSize property from the MyHiddenFieldPageStatePersister class. The code for this overridden method follows:

Visual Basic Version:

Protected Overrides Sub SavePageStateToPersistenceMedium(ByVal state As Object)
    MyBase.SavePageStateToPersistenceMedium(state)

    Dim myPersister As MyHiddenFieldPageStatePersister = CType(Me.PageStatePersister, MyHiddenFieldPageStatePersister)

    Response.Write(String.Format("

Total ViewState Size For This Page: {0:N0} bytes

", myPersister.StateSize)) End Sub

C# Version:

protected override void SavePageStateToPersistenceMedium(object state)
{
    base.SavePageStateToPersistenceMedium(state);

    var myPersister = this.PageStatePersister as MyHiddenFieldPageStatePersisterCS;

    if (myPersister != null)
        Response.Write(String.Format(@"

Total ViewState Size For This Page: {0:N0} bytes

", myPersister.StateSize)); }

Happy Programming!

Comments

No Comments

Archives

My Books

  • Teach Yourself ASP.NET 4 in 24 Hours
  • Teach Yourself ASP.NET 3.5 in 24 Hours
  • Teach Yourself ASP.NET 2.0 in 24 Hours
  • ASP.NET Data Web Controls Kick Start
  • ASP.NET: Tips, Tutorials, and Code
  • Designing Active Server Pages
  • Teach Yourself Active Server Pages 3.0 in 21 Days

I am a Microsoft MVP for ASP.NET.

I am an ASPInsider.