Remove ViewState From ASP.NET Page to Improve Performance

In a previous post, I eluded to the fact that I remove the ViewState from our ASP.NET pages to improve performance. I can’t take credit for coming up with the idea though. Originally, I got the idea and solution I wanted to implement by reading this article at EggHead Cafe. The solution I chose was to store the ViewState on the web server’s file system and reference the file in the page sent back to the client. I’ll outline how I did that below.

The first thing you’ll want to do is create a new Page base class that inherits from System.Web.UI.Page so you can override some of its methods, namely SavePageStateToPersistenceMedium and LoadPageStateFromPersistenceMedium. This will allow you to save the page’s ViewState to the file system and then re-load it on a post back. Let’s start with saving the ViewState to the file system.

protected override void protectedSavePageStateToPersistenceMedium(object viewState)
{
	// Serialize the view state into a base-64 encoded string
        LosFormatter los = new LosFormatter();
	StringWriter writer = new StringWriter();
	los.Serialize(writer, viewState);

	string guid = NewGuid();
	string vsp = ViewStateFilePath(guid);

	// Save the string to disk
	StreamWriter sw = File.CreateText(vsp);
	sw.Write(writer.ToString());
	sw.Close();

	// Save the guid in a form field
	ClientScript.RegisterHiddenField("__VIEWSTATE_GUID", guid);
}

So, let’s step through what we’re doing here. The first few lines of code, we’re serializing the view state. Next, where we call

string uid = NewGuid();

We’re creating a new guid that we will use in creating the file name on the server for the actual value of the current view state. NewGuid() just returns a new guid value from the System.Guid class.

Next, we need to create a path to where we’re going to store the file as well as its file name. Now, you can do this any way you want as long as all of your files end up being unique. You can’t go overwriting one guy’s view state with someone else’s. Now I based mine on basically the guid appended with the current request path minus the extension and replacing the slashes with dashes. So, my filename looks like:

string fileName = guid + "-" + Path.GetFileNameWithoutExtension(Request.Path).Replace(
    "/", "-") + ".vs";

Where the guid was passed in to the function that I called to create the view state file path.

So now that we have a path to where we can write the file, we can go ahead and do so using the StreamWriter that we created. Now, the last thing to do is spit out where we can find the view state to the client. This is done by registering a hidden field with the client:

ClientScript.RegisterHiddenField("__VIEWSTATE_GUID", guid);

That GUID allows you to recall the proper file for the client when you need to access the ViewState.

So now you’ve persisted the view state to the file system. Now all that’s left is to load it up when needed, which is done with LoadPageStateFromPersistenceMedium(), which is below.

protected override object LoadPageStateFromPersistenceMedium()
{
	string guid = Request.Form["__VIEWSTATE_GUID"];
	if (guid == "")
		return null;
		// determine the file to access
	if (!File.Exists(ViewStateFilePath(guid)))
	{
		return null;
	}
	else
	{
		// open the file
		StreamReader sr = File.OpenText(ViewStateFilePath(guid));
		string viewStateString = sr.ReadToEnd();
		sr.Close();
		// deserialize the string
		LosFormatter los = new LosFormatter();
		return los.Deserialize(viewStateString);
	}
}

This approach gives you the ability to minimize the amount of data that you have to send over the wire to a client browser. There are other solutions to this like storing the ViewState in memory, but I felt that it was a waste of resources if a ViewState might only be used once and then abandoned. The one thing you’ll want to do is manage your saved ViewState files on your disk. These files can get out of control, especially when you get a lot of visitor’s. So what I did was just set up a Scheduled Task in Windows to clean up stored ViewState’s every three hours. This works out pretty good since a ViewState really won’t be needed beyond that timeframe.

Anyway, I hope this solution helps some other C# ASP.NET developers out there. It sure worked out great for me!

ASP.NET Site whitehouse.gov Reviewed

So I was over on Slashdot last night looking for something interesting to read and ran across this tidbit about the new whitehouse.gov site that runs on ASP.NET. Honestly I think the only reason this got mentioned on Slashdot is that yesterday was Inauguration Day. Any other day and it probably would have fallen through the cracks. Anyway, I decided to take a look at the pointers that the author brought up to see if there was something I could learn. Most of the improvements I already knew about, but there were a couple that were new to me. Two that I’d like to consider implementing in my sites are:

  • Remove X-Aspnet-Version: header (remove 30 bytes per request)
  • Use compressed JQuery from Google’s servers (lower latency and improve performance)

The one issue with using JQuery from Google is your tied to whatever they’re using. If a newer version comes out and you want to use it but Google doesn’t, you couldn’t do this. Luckily I use JQuery 1.2.6, so this isn’t an issue for me.

As for the author’s review, I thought it was pretty good. A real world example is always a great illustration of what to do and what not to do. I’d have liked to see him make a suggestion on how to fix one of the issues he found, the ViewState issue, which would have been useful for other developers making the same mistake.

ViewState is still necessary for an ASP.NET application, however you don’t have to pass the entire ViewState back to the client. Its a waste of bandwidth (and can ’cause some nasty Base64 exceptions). One solution I use is to store the ViewState on the file system and only pass the file name back to the client for reference later on. It takes up a lot less bandwidth than a potentially huge ViewState. Other solutions are storing it in memory, the database, or by some other means. We clean out our old ViewState’s every 3-4 hours as many of them aren’t needed after that point (I’m thinking this might make a good article in the future).

Another example that kind of irked me was the site’s use of meta keywords. Uhm, yeah, this might not be as relevant anymore for Search Engine Optimization, but its still not a bad thing to have it in there. Just keep it to 200-300 characters. Nothing too crazy.

One last thing that he pointed out that I just didn’t agree with was that the server was running IIS6.0. Now, correct me if I’m wrong, but IIS7.0 is only supported on Windows Server 2008, right? Well, IT departments have budgets and all, so maybe a Windows Server 2008 license or the hardware to install it to wasn’t available? I know in my case, the budget doesn’t always allow for the latest and greatest, even if I want to use it. So to knock the development team for using IIS6.0 seems a little over the top if you ask me.

This entire site could definitely use some improvements, which the article nicely points out. To go one step further, I suggest any web developer install YSlow for Firebug for the Firefox browser. This came in handy for me when I was trying to optimize my sites. The whitehouse.gov site’s YSlow score is a 51 (an F), which is horrible. When I started using YSlow, I noticed some of my sites had a similar score, to which I was appalled and went to fixing pronto. By implementing some of the changes that I could as suggested by YSlow, I got us up to a 78, which is a high C. Some changes you can’t make (like Javascript at the bottom of pages and using a Content Delivery Network) due to how your application works and changing them just to make a minor improvement is more trouble than its worth. However, there isn’t any excuse to have an ASP.NET site that scores so low. Those folks over at whitehouse.gov definitely need to clean things up!

URL Rewriting in ASP.NET – ISAPI Rewrite vs. UrlRewritingNet

Seeing your web site rank well in the major search engines (Google, Yahoo, MSN) is something that every web developer strives for. Making sure your web site’s pages are search engine friendly is a huge part of that effort. With ASP.NET, pages typically end in the .aspx extension. While this isn’t bad for SEO, most ASP.NET pages are also dynamic. So having a dynamic page that is rendered based on a bunch of query string variables doesn’t get you anywhere with SEO, especially if you use a lot of them. So for a while now, we’ve used ISAPI Rewrite to rewrite your dynamic pages into something that is a lot more user friendly. This ISAPI extension isn’t free though. It costs $99 to purchase a license. Not that bad, right?

Well recently, I discovered an Open Source solution called UrlRewritingNet. It was originally developed in 2006 for the 2.0 framework, however the developers claim it will work all the way up to the 3.5 framework. I’m not sure how much further development is being done though, as the last release was in August of 2006. It is Open Source, so theoretically you can download the source and make modifications yoruself if you need to.

UrlRewritingNet integrates directly into your ASP.NET web application as a HttpModule. With each incoming request, this module is called to see if the URL requested is a rewritten URL. This isn’t too different from ISAPI Rewrite except that the rewrite for ISAPI Rewrite is handled higher up the stack by IIS itself and not the web application. Using one solution over the other shouldn’t be much different from a performance standpoint, but tests would need run to prove it. That’s a task for another time however.

The one drawback, for me anyway, was that I did see that UrlRewritingNet requires all of the rewrites to be defined in the Web.config file. I’m not a huge fan of that. Web.config gets cluttered up enough as it is, so the more rewrites you need the bigger this file is going to get. With ISAPI Rewrite, all of the rewrites are stored in a separate file called httpd.ini, much like rewrites in Apache. On the plus side, it looks like you can extend UrlRewritingNet by developing your own rewrite rule providers. So if you need some functionality that isn’t provided, you can hook what you need up yourself.

UrlRewritingNet looks pretty promising and I hope I have some time to check it out and see if it is a good substitute for ISAPI Rewrite.

Minimizing Downtime When Deploying ASP.NET Web Applications

One of the annoying things I find with managing our ASP.NET web applications is that I need to “shut down” our sites when deploying a new version of our assemblies. Its not that I’m worried about visitors seeing our maintenance page for 20-30 minutes, but as search engines spider our site during this down time, they can’t find pages that they knew about before. This is especially annoying when analyzing my sites in Google’s Webmaster Tools and see that I have a bunch of HTTP errors because Google couldn’t find a page because the site was unavailable. Since Google puts a high regard on site availability and quality when determining rankings, I’d like to avoid this.

Deploying the site is actually very simple. We use the XCOPY method of pushing up web forms, controls, and assemblies. But if you just start overwriting files in the live site, users get errors or inconsistent pages. And, if any database changes need to be made, code could not function properly before updating the site. Any of these problems would affect my Google issue I mentioned above as well. Not only that, but any developer worth his/her paycheck tests their changes in the live environment anyway. So just tossing up the new version is no good.

I’ve been considering a few different solutions to this, but I haven’t come up with something that solves the issue completely. At some point, I still have to take down the site.

One solution that I thought of was that I could set up a staging server to deploy my changes to and then run my tests there. Once I’m satisfied with the results, I could push it to my live site, minimizing the amount of down time. I figure the max amount of downtime using this approach would be 5-10 minutes depending on if I had to make any database changes. Not a perfect solution, but better than the 20-30 minutes I’m experiencing now.

Another solution I thought of was to set up a web farm. I could deploy the new version to one web server, make sure everything is good to go, then deploy it to the second web server. Users could still visit the site because at least one server in the farm could handle the incoming request. But this wouldn’t work great if database changes needed to be made. The site itself would still have to come down.

So right now, solution #1 appears to be the best approach and easiest to implement. Maybe I’m making more of a big deal of this than I need to, but I think everyone wants to minimize the downtime of their site. The one reason I’m holding back on changing my approach is I don’t know how much Google or other search engines weigh not being able to access a site for a short period of time. Regardless, I’m curious what other solutions developers and web teams use to deploy their ASP.NET applications to minimize site downtime. I’m positive there is a solution to my problem, but I just haven’t thought of it yet. If anyone has something that works for them, please chime in here!

Speeding Up Your Web Site with YSlow for Firebug

I’m always looking for an edge over our competitors to make using our e-commerce sites better from a usability standpoint. I think one of the easiest things to make the experience better is to make sure your site is responsive when people visit it, no matter what kind of connection they have or what they have for a computer. I decided to do some research on how to improve our sites download times and came across YSlow for Firebug.

YSlow is a Firefox extension that plugs into the Firebug extension. Any developer that doesn’t use Firebug is really missing out. So if you don’t have it, get it. Anyway, you can install YSlow right into Firefox and get access it through Firebug.

Upon analyzing our site the first time, we received a score of 42 from YSlow, which was an F. Ouch. That didn’t make me feel all that great about our site. You can see screen shots of our initial scores here and here. We scored really low for all but four of the thirteen performance criteria. I decided to attack the easiest tasks to complete first. This was Minify JS, Add an Expires header, and Gzip components.

I minified our javascript files using a utility called JSMin. It basically removes all whitespace and line returns from your file. It doesn’t compress the code all the way, but I wanted it to remain a little readable if I needed to look at the code on the live search.

Next, I wanted to handle adding an expires header. Since we use ASP.NET and C# for our web application, I was able to write a HttpHandler to do this for me. What was even better was I was able to handle the expires header and another issue, ETags configuration, all in the same snippet of code. For each request, our HttpHandler adds an empty ETag and an Expires Header of 3 days in the future. Both of these are used to determine when a cached copy of a web page needs to be refreshed. The ETag tells the browser that the version it sees now is different from the original. The Expires header obviously sets the expiration on the page.

Lastly, I wanted to GZip all of our components. This just required configuration of our IIS Server. You can also do this directly within your .NET application, but I didn’t see the value in this as IIS could do it for us.

After implementing these changes and a few other mundane ones, I ran YSlow again. Low and behold, we’d gone from a score of 42 to a score of 76. Not bad! We’re now scoring a “High C” according to YSlow. From a usability standpoint, I could definitely tell that the site responded much faster than it did when we were scoring a 42. For those of you that would like to see screen shots of the stats, you can see them here and here. Looking at the stats, you can see that we cut down the data downloaded from 413.1k to 234k, which looks like a huge improvement.

I strongly recommend anyone who’s developing web applications to take a look at YSlow. You might not be able to implement changes for all of the points it says you’re not doing well for, but even 2 or 3 changes should net you some great improvements in the performance of your site.

ASP.Net HyperLink Control And Html Encoded Ampersand’s

I just ran into some odd behavior with the HyperLink control ASP.Net. Per the W3C, you’re supposed to HtmlEncode ampersands, using & instead of ‘&’ when building URLs in your HTML code. The reason is that the ‘&’ is assumed to be an entity reference. What’s nice is most web browsers can recover from this type of error, but if you want your site to pass validation, you need to use & instead.

So I hooked up all of our URLs to use this method, especially when we wrote out URLs in our C# classes. What I found odd was if I did this using a HyperLink control instead of an HtmlAnchor control, .NET would write the & out in the URL instead of using ‘&’. Naturally this broke our site as query string references weren’t parsed properly. The fix was to use an HtmlAnchor instead.

I’m not really sure why .NET does this or if there’s another workaround for it, but this solution worked for me. I’d be curious to know the reason behind the behavior though.

Import Costumes Goes Live!

Our new e-commerce site, ImportCostumes.com, has gone live!  Its been months since development began and after lots of blood, sweat, and tears (mostly over PayPal account setup and integration; more on that to come later), we’re ready to take orders.  We have lots of cool men’s costumes and women’s costumes.  We also have a large selection of boy’s and girl’s costumes, and not to mention awesome accessories!  Come check it out and buy some stuff!

Weird IE6/ASP.NET SSL Bug

We found a weird IE6 bug in Fright Catalog and YumDrop today.  Turns out, when in the checkout process, IE6 would display a message to the user that they were navigating from a secure page to an insecure page.  Firefox, IE7, and Camino wouldn’t display any notification message at all.  The IE6 warning was totally bogus, since the resulting page was definitely secure (little gold lock, no messages, etc.).  For a while, I thought it was a bug in IE6, but the more I thought about it, that didn’t make much sense. 

That’s when I discovered that the notification only came up on a PostBack on a checkout page, not when the page first loaded.  So, knowing that we manage our secure pages using what we call a Redirect Manager, I took a look at that.  After some digging, I realized I was kind of barking up the wrong tree, and the real issue was the lack of a trailing slash on the URL.  This is something that we can either fix in our redirect or in the Secure Redirect module we use.  What I found really interesting though, is that this wasn’t an issue in Firefox, Camino, or IE7.

Frightcatalog.com Re-Launch First Impressions

We’ve had the new version of FrightCatalog.com up for a week now. We’ve had lots of great comments and feedback, but success is never really gauged by that. To that end, its good to see that a lot of hard work has paid off and I think we’re starting to see that from the numbers in Google Analytics

The first half of the month (July 1 – July 18) we saw 83,796 uniques, 9.27 pages/visit, and a 0.33% conversion rate.

The last week (July 19 – July 25), with the re-launch, we’ve seen 41,673 uniques, 12.00 pages/visit, and a 0.75% conversion rate.

Almost half the uniques in a 1 week period than we had in the first 18 days of the month. Page visits are up by almost 3.5, and our conversion rate has more than doubled. Good initial numbers after week one.

This is starting to prove that our de-facto framework for our e-commerce sites is pretty solid (all software can always be improved and we’ll continue to do so), and has a lot of promise. It’ll be nice to see how we do through the entire Halloween season with some new marketing initiatives in place. I hope this is a sign of good things to come in the coming months.

New FrightCatalog.com – 2007

Its been quite a long week. Late last night saw the coming of the latest edition of FrightCatalog.com. I think you’ll notice the more product centric slant its taken on this year. A little less glitz, a little more practicality. It should be MUCH faster too. Same framework as YumDrop.com and the soon to come ImportCostumes.com. Thanks for Kyle’s help, even though he’s moved on to a new venture.