Viewstate Compression When Using AJAX

3/6/2008

One thing that might annoy you with ASP.Net is the size of the viewstate when your using heavy controls (DataGrids, etc.). One thing to ameliorate this is to obviously turn off the viewstate for any item it isn't needed. However this can only save you so much space and it might not be enough. In cases where your bandwidth are still an issue you can try compressing the viewstate. This is actually very easy to do, just use a deflatestream to compress the data and save it as a hidden field on the page. In fact to show you this approach I've created two classes, Deflate and ZippedPage (note though that you need to have a scriptmanager on the page for them to work): 

ZippedPage

   1: #region Usings
   2: using System;
   3: using System.IO;
   4: using System.Text;
   5: using System.Web.UI;
   6: using System.Web.UI.HtmlControls;
   7: using Core.Configs;
   8: using BusinessObjects.Interfaces;
   9: #endregion
  10:  
  11: namespace BaseClasses
  12: {
  13:     /// <summary>
  14:     /// Base class that all pages are created from
  15:     /// </summary>
  16:     public class ZippedPage : System.Web.UI.Page
  17:     {
  18:         #region Protected Functions
  19:  
  20:         /// <summary>
  21:         /// Loads the view state from the compressed format
  22:         /// </summary>
  23:         /// <returns>Returns the uncompressed view state</returns>
  24:         protected override object LoadPageStateFromPersistenceMedium()
  25:         {
  26:             try
  27:             {
  28:                 LosFormatter Formatter = new LosFormatter();
  29:                 string vstate;
  30:                 vstate = this.Request.Form["__ZIPSTATE"];
  31:                 byte[] b = Convert.FromBase64String(vstate);
  32:                 b = Utilities.Compression.Deflate.Decompress(b);
  33:                 vstate = Convert.ToBase64String(b);
  34:                 return Formatter.Deserialize(vstate);
  35:             }
  36:             catch { return ""; }
  37:         }
  38:  
  39:         /// <summary>
  40:         /// Compresses the view state down and saves it
  41:         /// </summary>
  42:         /// <param name="state">The view state object</param>
  43:         protected override void SavePageStateToPersistenceMedium(object state)
  44:         {
  45:             try
  46:             {
  47:                 LosFormatter f = new LosFormatter();
  48:                 StringWriter sw = new StringWriter();
  49:                 f.Serialize(sw, state);
  50:                 byte[] b = Convert.FromBase64String(sw.ToString());
  51:                 b = Utilities.Compression.Deflate.Compress(b);
  52:                 ScriptManager.RegisterHiddenField(this, "__ZIPSTATE", Convert.ToBase64String(b));
  53:             }
  54:             catch { }
  55:         }
  56:     }
  57: }

The Deflate class is apart of my utilities library and I suggest going there to find the latest version but as of this posting, the deflate class looks like this:

Deflate.cs

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System.IO;
  24: using System.IO.Compression;
  25: #endregion
  26:  
  27: namespace Utilities.Compression
  28: {
  29:     /// <summary>
  30:     /// Utility class used for compressing data
  31:     /// using deflate.
  32:     /// </summary>
  33:     public static class Deflate
  34:     {
  35:         #region Static Functions
  36:  
  37:         /// <summary>
  38:         /// Compresses data
  39:         /// </summary>
  40:         /// <param name="Bytes">The byte array to be compressed</param>
  41:         /// <returns>A byte array of compressed data</returns>
  42:         public static byte[] Compress(byte[] Bytes)
  43:         {
  44:             using (MemoryStream Stream = new MemoryStream())
  45:             {
  46:                 using (DeflateStream ZipStream = new DeflateStream(Stream, CompressionMode.Compress, true))
  47:                 {
  48:                     ZipStream.Write(Bytes, 0, Bytes.Length);
  49:                     ZipStream.Close();
  50:                     return Stream.ToArray();
  51:                 }
  52:             }
  53:         }
  54:  
  55:         /// <summary>
  56:         /// Decompresses data
  57:         /// </summary>
  58:         /// <param name="Bytes">The byte array to be decompressed</param>
  59:         /// <returns>A byte array of uncompressed data</returns>
  60:         public static byte[] Decompress(byte[] Bytes)
  61:         {
  62:             using (MemoryStream Stream = new MemoryStream())
  63:             {
  64:                 using (DeflateStream ZipStream = new DeflateStream(new MemoryStream(Bytes), CompressionMode.Decompress, true))
  65:                 {
  66:                     byte[] Buffer = new byte[4096];
  67:                     while (true)
  68:                     {
  69:                         int Size = ZipStream.Read(Buffer, 0, Buffer.Length);
  70:                         if (Size > 0) Stream.Write(Buffer, 0, Size);
  71:                         else break;
  72:                     }
  73:                     ZipStream.Close();
  74:                     return Stream.ToArray();
  75:                 }
  76:             }
  77:         }
  78:  
  79:         #endregion
  80:     }
  81: }

When I started using AJAX, I noticed that items weren't being updated during an async postback any more if I was using the original versions of these classes. It took me about 2 days to figure out that the issue was that the scriptmanager had to have the hidden field registered to it... Specifically this line had to be added:

   1: ScriptManager.RegisterHiddenField(this, viewstatename, Convert.ToBase64String(ViewStateBytes));

So if you've created something similar to this and were running into that issue, that line will fix your issues. Anyway, try out the code and hopefully you'll learn something or perhaps you can show me a better way. As always comments are welcome.



Comments

Tahri Akram
January 27, 2012 4:17 AM

I'm using compressing/decompressing viewstate module in my project. I got the problem when i used update panel. It took my two days to figure out what is the problem. I was registering hiddenfield with ClientScript. I replaced it with ScriptManager and my problem solved.Thanx.Tahir Akram