Know Your Friends Well, Know Your Enemies Better

Long time ago I had to deal with a case where the famous ViewState generated by WebForms was quiet heavy. This post is not going to wine about how bad ViewState is for the environment. This post is to show that even working with something big and nasty such as web forms, you still have to give a thought what are you trying to do and what is out there that can help you to accomplish the mission.

Before getting into the "solution" (well, it is a solution, but kind-of stinky if you are asking me), I would really recommend the post about what is ViewState and it's influence on your nervous system.

So what is solution - not to use ViewState :) Weak solution.

Spike and invent something - bad idea. The wheel in already invented.

Spike on WebForms implementation and learning - that's the one.

 

First we need to look at the "container" of the ViewState which is nothing but a Page. This is where one of my favourite tools for reflection is making its' big entrance - Reflector. Looking at the page, you shall see that a page has an interesting getter called "PageStatePersister".

 Page class using reflector

Digging deeper provides an insight into the intension of this property:

   1:  protected virtual PageStatePersister PageStatePersister
   2:  {
   3:      get
   4:      {
   5:          if (this._persister == null)
   6:          {
   7:              PageAdapter pageAdapter = this.PageAdapter;
   8:              if (pageAdapter != null)
   9:              {
  10:                  this._persister = pageAdapter.GetStatePersister();
  11:              }
  12:              if (this._persister == null)
  13:              {
  14:                  this._persister = 
  15:                      new HiddenFieldPageStatePersister(this);
  16:              }
  17:          }
  18:          return this._persister;
  19:      }
  20:  }

A few interesting things in particular we can learn from the code:

  1. The default state persister fro ANY page is a HiddenFieldPagePersister, the one that generates a hidden field with all the gibberish
  2. A page can have an Adapter that could potentially redefine what's the default persister for pages (bookmark this concept for a while, for people like myself, write it down on a piece of paper with a title "review this later")
  3. Regardless of the fact what persister is going to be used, this is a Template Method that acts as a Factory to return back an object that was derived from an abstract PageStatePersist class

What a world of opportunities in such a limited world of ViewState! Let's use reflection to find out what are the possibilities. And the winners are:

PageStatePersist class 

Not that much of options, but hey, we can have an option of keeping it at "home" on the server side, rather than sending to the client EVERY-SINGLE-ROUNDTRIP!

There are a few options:

  1. Act on a page level and override the Templated Method to return an instance of SessionPageStatePersister rather than HiddenFieldPageStatePersiter
  2. Create an adapter for the entire application
  3. Choose between option 1 or 2 with a custom persister class, inherited from abstract PageStatePersist

Option 3 is for your wild and kinky imagination, option 1 is too trivial, option 2 is what I would like to expand on a bit more.

So we want to reconfigure the entire web application for a different persister (Session one for the application that has lots of stuff in ViewState, and should produce a slim HTML). Adding a folder App_Browsers allows us to register different controls adapters. Page is a control as well, see for yourself:

image

In this folder, we can specify adapters per browser, or all of them (ahh, my old days of WAP development with Mobile ASP.NET are bubbling up, causing me a horror moment). Something like BrowserFile.browser file should do it - use VS.NET add new item option to add a .browser file. Then register an adapter for a page. The adapter code would be simple as this:

   1:  using System.Web.UI.Adapters;
   2:  using System.Web.UI;
   3:   
   4:  public class SesionPageStateAdapter : PageAdapter
   5:  {
   6:    public override PageStatePersister GetStatePersister()
   7:    {
   8:      return new SessionPageStatePersister(this.Page);
   9:    }
  10:  }

.browser file would be a trivial mapping guided by the Intellisense as you type:

   1:  <browsers>
   2:   <browser refID="Default">
   3:     <controlAdapters>
   4:        <adapter controlType="System.Web.UI.Page" 
   5:                 adapterType="SesionPageStateAdapter" />
   6:      </controlAdapters>
   7:     </browser>
   8:  </browsers>

Unfortunately it will not remove the ViewState completely, but it will definitely minimize it. Sometime several dozens of KB can be prevented from going back and forth. Also keep in mind that ControlState (from ASP.NET 2.0 and later) is possible contributing, and that one is not disableable.

Hopefully this helps to those who are in pain of watching hundreds of Kilobytes traveling there, taking more "page weight" than the page content itself.

3 Comments

  • You could always use compression (system.io.compression). I have used it on pages that have html editors and it really helps. Keeping page state with the page is really a good idea. Your going to loose alot if you remove it from the page.

  • Nice solution which I like more than overriding Save/LoadPageStateFromPersistenceMedium.

    Though I would NOT recommend to use Session for that kind of operations. It's much better to write your own adapter.

    But, the fun is when your application gets a decent load, you will want to put as much things as you can on the client.
    Several dozens of kilobytes added to bandwith is nothing comparing
    having server filled with 1000 sessions (which live 20 minutes by default each) containing several dozens kilobytes each.

  • Sean thank you for your answer. Surely there are different scenarios, which require different solutions.

    I just wanted to notice, that using session in ajax applications leads to several drawbacks as well. So my main point were that session isn't good, and in some cases its better to leave viewstate on the client. But the solution with adapter is very elegant imho.

    Also the funny thing I met dealing with high load applications, that they use to give a client almost blank html, and the UI is composed on the client with javascript.

Comments have been disabled for this content.