Viewstate On The Server

posted on 03/23/06 at 09:40:56 pm by Joel Ross

We're going through an existing application with an eye on performance. One of the first things we noticed was viewstate. We had quite a few pages that were pretty heavy, so we started looking at what we could do about it.

The obvious choice was to reduce the size, but that takes time to determine what can be excluded, and with a?lot of pages, that would be a fairly large undertaking. We plan to do that over time, but we wanted a speed increase now. So we started looking at alternatives to storing the viewstate on the client. Our application is hosted in the midwest, but the users are primarily on the left coast, and it's accessed over a T1 - it's not available on the 'Net, so the pipe is limited. With 300 or so users, it's important for us to limit the sheer amount of data passed over the line. We eventually settled on storing viewstate in the database. We looked at some alternatives - cache, session, etc., but settled on the database because, while it isn't as fast as the other two, it is less memory intensive, and still faster than passing it over the wire.

So how did we do it? Well, we were smart and put in a base page that all pages in our application use, so we only had to modify one file, and all pages benefited. Then we added a web.config option to turn it on and off, and left it flexible enough that we could add other storage mechanisms in the future.

With that, here's the code to save it to the database:

   1:  protected override void SavePageStateToPersistenceMedium(object state)
   2:  {
   3:      switch (ConfigurationManager.AppSettings["ViewStateMode"].ToLower())
   4:      {
   5:          case "database":
   6:              LosFormatter los = new LosFormatter();
   7:              System.IO.StringWriter writer = new System.IO.StringWriter();
   8:              los.Serialize(writer, state);
   9:              string viewState = writer.ToString();
  10:  ?
  11:              string key = ViewStateManager.InsertViewState(viewState);
  12:  ?
  13:              RegisterHiddenField("__VIEWSTATE_KEY", key);
  14:              break;
  15:  ?
  16:          default:
  17:          case "normal":
  18:              base.SavePageStateToPersistenceMedium(state);
  19:              break;
  20:      } 
  21:  }

When storing viewstate in cache or session, you don't have to worry about serializing the data - you can throw it in, and pull it out?as an object without issue. When you throw it in the database, you need to serialize it to a string, so you have to use a LosFormatter to do that for you. Our ViewStateManager takes a string and returns a key - we use a GUID for our key, but I've also seen using the session ID combined with the current page name and time. It doesn't really matter what you use, as long as it't unique. Then, you register that key as a hidden field. So instead of passing the whole viewstate to the client, you only pass a pointer to it. Now, when a page posts back, you use that key to load it from the database:

   1:  protected override object LoadPageStateFromPersistenceMedium()
   2:  {
   3:      switch (ConfigurationManager.AppSettings["ViewStateMode"].ToLower())
   4:      {
   5:          case "database":
   6:              string key = Request.Form["__VIEWSTATE_KEY"];
   7:              string viewState = ViewStateManager.GetViewState(key);
   8:  ?
   9:              LosFormatter los = new LosFormatter();
  10:              return los.Deserialize(viewState);
  11:          
  12:          case "normal":
  13:          default:
  14:              return base.LoadPageStateFromPersistenceMedium();
  15:      }
  16:  }

As you can see, we can easily turn it off in the web.config and have viewstate back in the page. We considered using our configuration table to manage this value so we could change viewstate persistence on the fly, but determined that would actually be a bad thing. Imagine this scenario:

  1. User loads a page and viewstate is stored in the database
  2. The configuration is changed to persist viewstate to the page
  3. User posts back

So what happens? When the user posts back, the load viewstate method will be looking for the viewstate in the page, when it's actually in the database. Not good. At least with storing it in the web.config, it will force the application to restart, which gives us a little more protection - plus, it requires a developer to make the change, who should know the consequences of making that change.

The last thing to touch on is the viewstate table itself. Not the structure - that's pretty easy. Three fields: key, value, and a timestamp. Simple, but the most important thing to consider about this table is clean up. When do you delete the records in this table? Our initial thought was to delete it when you retrieve it. Once you've used it, it's no longer valid, right? Have you ever posted a form, had something go wrong, and just hit F5 to repost the data? I have, and if after thefirst post, the viewstate was deleted, hitting F5 would cause an error because the viewstate would be gone. That's a problem. So we decided to go with a nightly job to purge old records from the table. Since it's an internal application, we could reliably remove records at night knowing that most of the time, no users are currently using the site.

So while this is all technically possible, was it worth it? Using Fiddler, I went through a few different scenarios and my quick eye ball glance says page size was reduced by about 25% on a lot of pages, and on a few pages, the size was reduced by more than half. And this was after we've already implemented HTTP compression. Also, by not having to compress the viewstate reduces the tax on the server too - less data to compress.

Overall, we've been happy with the performance increase.

Categories: Development