.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!