.NET: Increase Application Perf with ViewState Persistence Tweaks and Optimizations

I was amazed most of us hate ASP.NET ViewState for its default poor implementation which if not taken into cosideration would kill our applications performance, client resources, and drive shoppers away. Its frutrating...

If  worst case you haven't heard of ViewState and I assume you are probably new to ASP.NET, I'll give a proof. Try to create a new website, put 5 buttons and 10 drop down list controls, add 20 items on each of controls. Run the application then view source on IE/FF and you will be surprised whatdahell are those squared block of cryptic characters in the page. That is the ViewState!!! ASP.NET uses this as stateful mechanism to compensate the stateless nature of HTTP. When you click a button and it postbacks, these viewstate is sent /posted into the server in the form of hidden control "__VIEWSTATE". ASP.NET runtime then deserializes this on server so that you can use these for server processing such as the value of textboxes, selected index on dropdown list, and contents of datagrids, repeater controls and radio buttons. After server processing, the runtime serializes this again and register the string value (in base64 form) into the hidden filled mention above as part of the response stream. Runtime uses special class called LosFormatter for these actions. For truely understanding viewstate click here.

In my case that I'm working on an e-commerce platform, this is very critical. A slow site is not usable at all, simly no customers are patient enough to wait for your page to load. Researching on it I have these attempts:

My first attempt is to compress the viewstate before sending to client and decompress when the page is posted.

    1         protected override object LoadPageStateFromPersistenceMedium() {

    2             string viewState = Request.Form["__VIEWSTATE"];

    3             byte[] bytes = Convert.FromBase64String(viewState);

    4             bytes = GZip.Decompress(bytes);

    5 

    6             LosFormatter formatter = new LosFormatter();

    7             return formatter.Deserialize(Convert.ToBase64String(bytes));

    8         }

    9 

   10         protected override void SavePageStateToPersistenceMedium(object state) {

   11             LosFormatter formatter = new LosFormatter();

   12             StringWriter writer = new StringWriter();

   13             formatter.Serialize(writer, state);

   14 

   15             string viewState = writer.ToString();

   16             byte[] bytes = Convert.FromBase64String(viewState);

   17             bytes = GZip.Compress(bytes);

   18             viewState = Convert.ToBase64String(bytes);

   19 

   20             ClientScript.RegisterHiddenField("__VIEWSTATE", viewState);

   21         }

This really make a big difference in viewstate and page size. A page originally 127KB turns 94KB! But I'm not convinced, there has to be something more.

My second attempt is to save the session on the server as suggested by tuldoklambat here. When I found out that manipulating ViewState is as simple as overriding two page events LoadPageStateFromPersistenceMedium and SavePageStateToPersistenceMedium, I can't help myself get my hand dirty with it.

    1         protected override object LoadPageStateFromPersistenceMedium() {

    2             string page = Request.Url.Segments[Request.Url.Segments.Length - 1];

    3             return Session[page + "-" + "VIEWSTATE"];

    4         }

    5 

    6         protected override void SavePageStateToPersistenceMedium(object state) {

    7             string page = Request.Url.Segments[Request.Url.Segments.Length - 1];

    8             Session[page + "-" + "VIEWSTATE"] = state;

    9 

   10             ClientScript.RegisterHiddenField("__VIEWSTATE", "dk.shopper.dk");

   11         }

In this case I use the current pagename + the ViewState hidden field key as key to the session variable. Several samples on the web with same approch failed to handle popup windows and windows with blank url target because they save the viewstate into session with the same key. Eeach page has a different viewstate, each must be independent with each other. Scott Hanselman did the same thing here but in my case I just use the page name.

This approach makes my pages load exteremely fast! As in flashing in localhost and less than a second on test server on a 512 kbps connection. When you view the source of the page, you will see that the value fo viewtstate variable is "dk.shopper.dk" only.

Here's another interesting implementation from aspalliance.com to solve the same issue with same session key.

Unfortunately all of these attempts failed me as I encountered intermittent exceptions due to corrupted viewstate on some pages. "The viewstate is invalid for this page and might be corrupted." It was thrown in random and darn so hard to reproduce and debug. My last attempt is to search for ASP.NET adapters and good thing I found out a sessionpage adapter is already available with ASP.NET 2.0. The codes goes like this:

First add this class into your web project.

    [CodeFile="AllBrowserPageViewStateAdapter.cs"]

    1 using System;

    2 using System.Web.UI;

    3 using System.Web.UI.Adapters;

    4 

    5 namespace MultishopAdmin.Web.Adapters {

    6     [Serializable]

    7     public class AllBrowserPageViewStateAdapter : PageAdapter {

    8         public AllBrowserPageViewStateAdapter() {

    9         }

   10 

   11         public override PageStatePersister GetStatePersister() {

   12             return new SessionPageStatePersister(this.Page);

   13         }

   14     }

   15 }

   16 


Next create folder app_browsers and add this file.

    [CodeFile="
AllBrowserPageViewStateAdapter.browser"]

    1 <browsers>

    2     <browser refID="Default">

    3         <controlAdapters>

    4             <adapter controlType="System.Web.UI.Page" adapterType="MultishopAdmin.Web.Adapters.AllBrowserPageViewStateAdapter"></adapter>           

    5         </controlAdapters>

    6     </browser>

    7 </browsers>

    8

This may not be as fast as my second attempt but I havent encountered viewstate corruption exceptions since then. There still some viewtstate passed into the client but very small, some pages only have few characters on viewtstate variable.

More to say on performance tuning web app but those deserves a new post. Happy coding!

Share this post: Email it! | bookmark it! | digg it! | reddit!| kick it!

Comments

# cruizer said:

great material, rodel. :) why didn't you just disable the viewstate altogether? :) is the page such a monster?

Wednesday, December 13, 2006 5:56 PM
# Jon Limjap said:

Maybe you could try storing the viewstate serverside. Check this out:

http://aspalliance.com/articleViewer.aspx?aId=72

Marami pang ibang implementations niyan around.

Wednesday, December 13, 2006 8:18 PM
# dehran ph said:

@cruizer

No i can't do that because I have datalist and lots of user controls. The page is not that heavy actually its just my client's performance requirements is "No Waiting". ;)

@jon

that artice from aspalliance is actually my third option but it still causes me intermittend viewstate corrupted exceptions when buttons post back.

Sunday, December 17, 2006 6:25 AM