Coding Horrors: ASP.net Viewstate

August 11, 2008 by C#   ASP.NET   Coding Horrors  

ASP.net at the bottom of it all mainly builds on sessionless technologies. The object instances on a web form essentially dies as soon as our rendered output reaches the browser.

Since we can't work with dead objects, Microsoft came up with a mechanism that "hibernates" objects and "re-awakens" them as soon as we need them.

This mechanism is called viewstate, which primary function is to persist the state of web forms, across postsbacks.

The web form contains a hidden field, which houses a serialized copy of the last state of the page.

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTkwNjc4NTIwMWRkemG0USJ6hDi/MgWFObHK2J15A3w=" />

During postback, this field gets passed along with the page request. The field then gets deserialized and re-instantiated into it's various objects.

Typically changes we made programmatically to objects, gets stored inside the viewstate (among other things).

This is were the catch comes in, we might end up with a behemoth of a viewstate if we're not careful - sacrificing a great deal of bandwidth.

To give you an idea, I once saw viewstate big enough to fill my 10mb harddrive back in the day.

This week alone I saw a 1.7mb page on one of our systems, due to its viewstate. (that page alone consumed 4.7GB of bandwidth that day)


How does one go about solving this?

Looking to the web, I've seen a few interesting solutions around this issue. The most popular solution involves overriding the LoadPageStateFromPersistenceMedium and SavePageStateToPersistenceMedium methods on the page class.

Instead of sending the viewstate along with the page, they store it on the server's harddrive or even more evil in the session.

protected string ViewStateFileName
{
      get
      {
            string vKey = (String.IsNullOrEmpty(Request.Form["viewstatekey"])) 
                    ? Guid.NewGuid().ToString() : Request.Form["viewstatekey"];
            ClientScript.RegisterHiddenField("viewstatekey", vKey);
            return ResolveUrl("~/viewstate/" + vKey);
      }
}
 
protected override object LoadPageStateFromPersistenceMedium()
{
      if (File.Exists(ViewStateFileName))
      {
            using (StreamReader stream = new StreamReader(ViewStateFileName))
            {
                  LosFormatter formatter = new LosFormatter();
                  return formatter.Deserialize(stream);
            }
      }
      else
      {
            return null;
      } 
}
 
protected override void SavePageStateToPersistenceMedium(object state)
{
      using (StreamWriter stream = new StreamWriter(ViewStateFileName))
      {
            LosFormatter formatter = new LosFormatter();
            formatter.Serialize(stream, state);
      }
}

If you feel the urge to store viewstate on disc (like my example at the top), you will need to consider a few things.

You will need to periodically clear the folder containing viewstate, which might prove to be quite an ugly process if you're sitting with hundreds of thousands files. Forcing us to clear this folder according to some kind of time interval (unless you're looking at using this on the odd page?).

Your time interval also needs to be quite selective when it comes to which files needs to be deleted, since you might end up deleting an active viewstate.

Looking at the approach of storing viewstate in the session, is simply a horrible idea from a memory consumption point of view.

One might still get away with it in a small web application, but on much larger busier applications, you might end up running out of memory.

I am not a huge fan of any of these approaches, mainly because I believe that one should be a minimalist when it comes to viewstate - only use it when really needed (look into technologies like Ajax for example). Viewstate isnt supposed to be gigantic to start out with.

In fact, with a few small suggestions without sacrificing any functionality, we managed to bring that 1.7mb page down to a mere 35kb, without overriding ASP.net's default viewstate behaviour.

Ideally if I am going to override viewstate functionality, it will solely be to provide me with a warning if viewstate gets too big.

In the following code we see just that, if the viewstate is larger than 32k, an exception gets thrown.

protected override void SavePageStateToPersistenceMedium(object state)
{
	if (Debugger.IsAttached)
	{
		using (MemoryStream stream = new MemoryStream())
		{
			LosFormatter formatter = new LosFormatter();
			formatter.Serialize(stream, state);
			if ((stream.Capacity / 1024) > 32)
			{
				throw new Exception("Please optimize viewstate");
			}
		}
	}        

	base.SavePageStateToPersistenceMedium(state);
}


Leave a Comment


Re:Deprecated October 1, 2009 by Anonymous

Thanks a lot..

Deprecated October 1, 2009 by Christoff Truter

RegisterHiddenField is a deprecated method, so future versions of your code might not work. Its available via Page.ClientScript.RegisterHiddenField now.

October 1, 2009 by Anonymous

Can you tell me if i replace RegisterHiddenField with ClientScript.RegisterHiddenField wil my code run into any issues?

Monster September 29, 2008 by Saurus

Very nice blog post on alternative view state management