Introduction

Recently, I developed a huge ASP.NET page, with more than 30 controls. As we all know, it's a good idea to disable the ViewState for the controls that don't actually need it, say Literals or Labels. After doing that, I noticed that the hidden ViewState field was still a few KBs big. This is obviously a big problem for the users that still don't have a broadband connection, because uploading 40 KB to the server is really a bad issue, especially when they begin to click the "Submit" button again and again because they don't notice any response. So, after a few searches through the Internet, I built a simple solution to compress the ViewState and therefore save a rough 50% of the bandwidth. This post by Scott Hanselman has been particularly useful. Although it's possible to use external libraries to perform compression tasks, I think the better solution is to use the GZipStream or DeflateStream that the .NET Framework 2.0 includes.

Compressing and Decompressing Data in Memory

First of all, we need a way to compress and decompress an array of bytes in memory. I put together this simple static class that exposes two methods: Compress and Decompress. The two available classes, GZipStream and DeflateStream, according to MSDN, use the same algorithm, so it's irrelevant which one you choose.

The code below is really simple, and doesn't need further explanation:

You need to save this class in a .cs file and put it in the App_Code directory of your ASP.NET application, making sure it's contained in the proper custom namespace (if you don't specify any namespace, the class will be available in the built-in ASP namespace).

Compressing the ViewState

Now, we can actually compress the ViewState of the page. To do that, we have to override the two methods LoadPageStateFromPersistenceMedium and SavePageStateToPersistenceMedium. The code simply uses an additional hidden field, __VSTATE, to store the compressed ViewState. As you can see, by viewing the HTML of the page, the __VIEWSTATE field is empty, while our __VSTATE field contains the compressed ViewState, encoded in Base64. Let's see the code.

In the first method, we just decode from Base64, decompress and deserialize the content of the __VSTATE, and return it to the runtime. In the second method, we perform the opposite operation: serialize, compress, and encode in Base64. The Base64 string is then saved into the __VSTATE hidden field. The LosFormatter object performs the serialization and deserialization tasks.

You may also want to create a new class, for example, CompressedPage, inheriting from System.Web.UI.Page, in which you override the two methods and then inherit your page from that class, for example MyPage : CompressedPage. Just remember that .NET has only single inheritance, and by following this way, you "spend" your only inheritance chance to use the ViewState compression. On the other hand, overriding the two methods in every class is a waste of time, so you have to choose the way that best fits your needs.

Performances and Conclusions

After a few tests, I noticed that the ViewState has been reduced from 38 KB to 17 KB, saving 44%. Supposing you have an average of 1 postback per minute per user, you could save more than 885 MB of bandwidth per month on every single user. That's an excellent result: you save bandwidth (and therefore money), and the user notices a shorter server response time.

I wanted to point out that this solution has a performance hit on the server's hardware. Compressing, decompressing, encoding, and decoding data is quite a heavy work for the server, so you have to balance the number of users with your CPU power and RAM.

Comments and Discussions

In my scenario, i have a sharepoint list consisting of 16900 items that is shown up in a grid on the asp .net page. After adding your code i face an issue where in if i click on an item on the second page of the grid, the first page item opens up.

I came across this article. It's a great article. Thanks for sharing.I tried to play around with sample a bit. I found out that ViewState is really compressed if data is long enough. There are many times the size of ViewState data is grown after compression. For short data, the compression is not effeciently.

Here's my suggestions for optimizing codesnippets. First we insert a header (1-byte size) before the first byte of ViewState. This header tells whether ViewState data is compressed or not.

First of all the default compression of gzip isn't very good. Using a more efficient compression method could eliminate this problem and have a higher compression ratio.Second, your optimization would optimize for size but not for speed. My idea would be that instead of doing the actual compressing and then compare the size with the size of the uncompressed viewstate, the code would be faster to define a threshold that will generally mean that below that size threshold, compression isn't effective and should be skipped. so, if the size of the view state is for example smaller than 512 bytes then skip compression, otherwise compress it.

This way it would generally optimize for size without losing speed

PURPOSE: Delays program execution until designated condition is indicated.

Actually the Viewstate it's a great tool for Developing web App. but it can turn out to be a Performance trouble if it grows too much, your article it's a smooth workaround. Thanks for sharing it.it also reduces the chance of getting the horrible MAC problem.

Given that the streams I used here are always managed in RAM, I supposed that the use of using could be avoided because the objects are disposed at the end of their scope (I hope). At any rate, it's surely a good practice to use using.

I 've attempted to use this in an ASPNet 2.0 web application with AJAX 1.0 and found I get an ObjectDisposedException after selecting a grid row. I've used the correct ScriptManager reference for registering the hidden field. For some reason the stream is closed prematurely. The stack trace is

That's because you haven't taken into account "AsyncPostBacks". During every PostBack you need to Compress / Decompress the ViewState. This needs special handling during an AsyncPostBack where MS Ajax is involved. If you're using a differt Ajax Framework, sorry, I can't help you, but it's the same concept. You don't need to modify you Compress / Decompress routines, just the Restore/Save PageStateToPersistanceMedium.

Notice the bolded text in the "SavePageStateToPersistenceMedium" method. During an AsyncPostBack you need to re-register the HiddenField, however, you cannot simply just do this the same way. If you're in an AsyncPostBack, you need to use the ScriptManager methods to do this. If you're just in the first PageLoad or subsequent PostBacks, then using the ClientScript is fine.

Glad to help. I do have a small update though. There is no need to ever call Page.ClientScript if you have a ScriptManager because internally IT knows if it's within an AsyncPostBack and uses the ClientScript internally if needed, so you can change all code that does this:

To this (notice I am not checking if the current request is Async):System.Web.UI.ScriptManager.RegisterHiddenField(...);orSystem.Web.UI.ScriptManager.RegisterScriptBlock(...);orSystem.Web.UI.ScriptManager.RegisterScript(...);

Hi, nice code.I used it but i have some problems with it, (some controls loses the viewstate).I found a better way apply the same idea:Create a new class that inherits from PageStatePersisterand overrides the PageStatePersister property of the page.here is the class i have made:

Using the compression with AJAX 1.0, there is a problem because the viewstate is not updated in partial rendering (with UpdatePanel).

The solution is to replace the lineClientScript.RegisterHiddenField("__VSTATE", Convert.ToBase64String(bytes));with this code:ScriptManager.RegisterHiddenField(this, "__VSTATE", Convert.ToBase64String(bytes));

You might run into issue if some of the pages has scriptmanager and other dont so it might be better to check this condition too: See the code below: if (ScriptManager.GetCurrent(this) == null) { ClientScript.RegisterHiddenField("__VSTATE", Convert.ToBase64String(bytes)); } else { ScriptManager.RegisterHiddenField(this, "__VSTATE", Convert.ToBase64String(bytes)); }

why not just use server side viewstate? You save all the bandwith... and likelyt the cost of storing the state somewhere is less then compressing, sending, recieving and decompressing.... maybe when i have time(not likley) ill run some performance tests.If you are using 2.0, server side viewstate is super easy, in 1.1, it's slightly more involved, but still easy enough.

Yes, you can store the ViewState on the server, using the Session object, but I don't believe that's a good solution when you have many users.In my case I needed to compress the ViewState only in one page, so it's not a problem to compress and decompress it.

why use session? you can use DB, text file, whatever you want... jsut write an adapter for it.It works well for many users Agreed, the nice thing about your solution is the per page option, with 2.0 page adapters you have it for the app or not. What we do where I code most the time, is most pages/sections of the site are their own apps/virtual direcotries anyway. In any case, I like the article, code, and idea.

I don't think so. A DB is surely slower than in-memory compression, and using files is even worse because you would have to set a file for every user... I think using Session it's the only way other than compression.Moreover, using a DB or files introduces the new problem of managing expiration; on the other hand using Session would waste memory when the user goes away and the session expires after (default) 20 mins. If you have manhy "occasional" users you end up having hundreds open sessions but only a few of them are still used, wasting lots of memory.

My idea being that the method would be quite beneficial when mixed with using for example a server control that has some ajax uses. Think paging grid or something, where the return string itself could be rather large.So, passing the compressed string to the client to be handled, with no .net framework, and just a browser, will he be able to decompress the data and use it?

I don't know, but I think that GZip works at bit-level so it would be impossible or highly difficult to decompress a GZip stream using JavaScript.You may implement a simple compression algorithm and then decompress the data through JavaScript. You may consider RLE, but it doens't have high compression rates at all.

If you have big responses you should consider HTTP compression. I have written an article about it: Link. It uses GZipStream and it's really simple to implement.

I dont think its possible with javasdcript, anyway, i liked the idea of a page by page option so i made this property in my base page. private bool _CompressViewState = false; public bool CompressViewState { get { return _CompressViewState; } set { _CompressViewState = value; } }There is also a global key to turn it on and off globaly, if we see performance is quick enough with it. we will have to test becuase our site has a failry large number of simultaneous users for most the day, so the cost of compression decompression on the server for everything may be to costly, but if our devs are careful and pick the right places, this could save quite a bit.