How to compress viewstate in ASP.NET 2.0

Posted by: Suprotim Agarwal , on 7/30/2007, in Category ASP.NET
Views: 65927
Abstract: This article demonstrates how to extend the functionality of the new System.IO.Compression namespace to compress and decompress viewstate information in ASP.NET 2.0 pages.
How to compress ViewState In ASP.NET
 
Http is a stateless protocol. Hence the ‘page-state-information’ is not saved between postbacks. In ASP.NET, viewstate is the means of storing the state of server side controls between postbacks. It contains a snapshot of the contents of a page. The information is stored in HTML hidden fields.
The viewstate is quiet handy in most cases as it does a lot of work for you in saving the state of controls. However it comes with a cost. If your page contains a lot of controls holding a large amount of data, you can imagine the load when this data is transferred to and from the browser on each postback. It gradually increases the size of your page, thereby leading to performance issues. Well you can get around this problem by disabling viewstate for controls, where maintaining state is not required (EnableViewState=false). However, in scenarios, where maintaining the state of controls is required, compressing viewstate helps improve performance.
Using System.IO.Compression
 
In ASP.NET 1.1, developers used custom compression tools like ICSharpCode.SharpZipLib  to compress viewstate. ASP.NET 2.0 provides us with the System.IO.Compression namespace, which contains classes with functionality to compress and decompress streams. This namespace contains two classes called DeflateStream and GZipStream that provides methods and properties to compress and decompress streams.
Compressing/Decompressing using GZipStream
 
The compression functionality in GZipStream is exposed as a stream. In the code displayed below, we will create a class called ViewStateCompressor that contains two methods :
Compress(byte[] array) - Accepts a decompressed bytearray, compresses it and returns a compressed bytearray.
Decompress(byte[] array) – Accepts compressed bytearray, decompresses it and returns a decompressed bytearray.
The code has been commented to help you understand each method.
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;
using System.IO.Compression;
 
///<summary>
/// Summary description for ViewStateCompressor
///</summary>
public class ViewStateCompressor
{
      public ViewStateCompressor()
      {
            //
            // TODO: Add constructor logic here
            //
      }
 
    public static byte[] CompressViewState(byte[] uncompData)
    {
        using (MemoryStream mem = new MemoryStream())
        {
            CompressionMode mode = CompressionMode.Compress;
            // Use the newly created memory stream for the compressed data.
            using (GZipStream gzip = new GZipStream(mem, mode, true))
            {
                //Writes compressed byte to the underlying
                //stream from the specified byte array.
                gzip.Write(uncompData, 0, uncompData.Length);
            }
            return mem.ToArray();
        }      
    }
 
    public static byte[] DecompressViewState(byte[] compData)
    {
        GZipStream gzip;
        using (MemoryStream inputMem = new MemoryStream())
        {
            inputMem.Write(compData, 0, compData.Length);
            // Reset the memory stream position to begin decompression.
            inputMem.Position = 0;
            CompressionMode mode = CompressionMode.Decompress;
            gzip = new GZipStream(inputMem, mode, true);
       
 
        using (MemoryStream outputMem = new MemoryStream())
        {
            // Read 1024 bytes at a time
            byte[] buf = new byte[1024];
            int byteRead = -1;
            byteRead = gzip.Read(buf, 0, buf.Length);
            while (byteRead > 0)
            {
                //write to memory stream
                outputMem.Write(buf, 0, byteRead);
                byteRead = gzip.Read(buf, 0, buf.Length);
            }
            gzip.Close();
            return outputMem.ToArray();
        }
       }
    }
}
Utilizing the ViewStateCompressor class
 
Once we have created the functionality of compressing and decompressing viewstate, its time to use this functionality in our webpages. For this, create a CustomPage which inherits from System.Web.UI.Page. In the CustomPage, you will need to override the LoadPageStateFromPersistenceMedium() and SavePageStateToPersistenceMedium() to serialize and deserialize viewstate. We require this customization because we are compressing the viewstate.  All the other web pages in the application will inherit from this BasePage.
The LoadPageStateFromPersistenceMedium() method has been overridden here to decompress the bytes stored in hidden field “_CustomViewState”. The steps performed are as follows :
·         The compressed viewstate stored in this field is encoded as Base64 string.
·         The Convert.FromBase64String(viewState) converts it into byte array.
·         It’s time to call the DecompressViewState() method we created earlier.
·         This method returns a bytearray containing decompressesed data which is converted back to Base64 string.
·         Finally the LosFormatter class is used to deserialize the view state.
The SavePageStateToPersistenceMedium() method accepts a ViewState object. The Serialize() method of the LosFormatter class accepts a stream and the viewstate object. If you have already observed, we are performing a reverse operation of what we did in the LoadPageStateFromPersistenceMedium() method. We will first serialize, compress and then encode data in Base64. This Base64 string is then saved into the “_CustomViewState” hidden field.
The code has been given below :
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;
 
///<summary>
/// Summary description for CustomPage
///</summary>
public partial class CustomPage : Page
{
      public CustomPage()
      {
            //
            // TODO: Add constructor logic here
            //
      }
 
    // Serialize view state
    protected override void SavePageStateToPersistenceMedium(object pageViewState)
    {
        LosFormatter losformatter = new LosFormatter();
        StringWriter sw = new StringWriter();
        losformatter.Serialize(sw, pageViewState);
        string viewStateString = sw.ToString();
        byte[] b = Convert.FromBase64String(viewStateString);
        b = ViewStateCompressor.CompressViewState(b);
        ClientScript.RegisterHiddenField("__CUSTOMVIEWSTATE", Convert.ToBase64String(b));
    }
 
    // Deserialize view state
    protected override object LoadPageStateFromPersistenceMedium()
    {
        string custState = Request.Form["__CUSTOMVIEWSTATE"];
        byte[] b = Convert.FromBase64String(custState);
        b = ViewStateCompressor.DecompressViewState(b);
        LosFormatter losformatter = new LosFormatter();
        return losformatter.Deserialize(Convert.ToBase64String(b));
    }
 
  
}
 
There you go!! Use these classes and hopefully your ViewState size will be reduced by 30-40%.
References
There are a number of good resources I referred to for this article. A few of them are:
http://www.dotnetbips.com/articles/22d33d11-1a75-42c8-bbf6-ca1a345d3fcf.aspx
http://www.codeproject.com/aspnet/ViewStateCompression.asp
http://www.eggheadcafe.com/articles/20040613.asp
http://msdn2.microsoft.com
Conclusion
This article introduced you to a new namespace System.IO.Compression in ASP.NET 2.0. We went through the classes provided in this namespace. The GzipStream class contains functionality to compress and decompress streams. We used it to compress and decompress viewstate to improve performance of our web applications. I hope this article was informative and I thank you for viewing it.
 
Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce the The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this eBook aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on .NET Standard and the upcoming C# 8.0 too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Purchase this eBook at a Discounted Price!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Suprotim Agarwal, MCSD, MCAD, MCDBA, MCSE, is the founder of DotNetCurry, DNC Magazine for Developers, SQLServerCurry and DevCurry. He has also authored a couple of books 51 Recipes using jQuery with ASP.NET Controls and The Absolutely Awesome jQuery CookBook.

Suprotim has received the prestigious Microsoft MVP award for ten consecutive times. In a professional capacity, he is the CEO of A2Z Knowledge Visuals Pvt Ltd, a digital group that offers Digital Marketing and Branding services to businesses, both in a start-up and enterprise environment.

Get in touch with him on Twitter @suprotimagarwal or at LinkedIn



Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Shameek Bain on Wednesday, March 19, 2008 7:48 AM
I think the while condition is wrong here. It should be as while (byteRead > 0) instead of while (byteRead <= 0)


while (byteRead <= 0)
            {
                //write to memory stream
                outputMem.Write(buf, 0, byteRead);
                byteRead = gzip.Read(buf, 0, buf.Length);
            }
Comment posted by Michael Mason on Monday, April 14, 2008 9:39 AM
Good idea. However, this may not work so well if pages use UpdatePanels for partial updates.
Comment posted by Thomas Hansen on Saturday, April 19, 2008 5:39 PM
Way to go :)))
Comment posted by jhjhh on Friday, May 30, 2008 7:12 AM
jhjhg
Comment posted by Ragunathan.M on Saturday, July 12, 2008 6:29 AM
ya nice idea. this is very useful for me
Comment posted by Jeff Bragin on Tuesday, July 7, 2009 4:21 PM
IT DOES NOT WORK.

the minute i enabled paging on a gridview, I got an error "can't read a close stream" around this line:             byteRead = gzip.Read(buf, 0, buf.Length);

Comment posted by Wade on Wednesday, August 19, 2009 9:35 PM
Yeah I'm getting the same problem, "Cannot access a closed Stream" around the same line. Any suggestions?
Comment posted by Yogish M S on Friday, October 9, 2009 3:49 AM
try without using Using,
Comment posted by suhair on Friday, April 23, 2010 3:04 AM
I can get the same problem ; "Cannot access a closed Stream". around this line     byteRead = gzip.Read(buf, 0, buf.Length);
Comment posted by suhair on Wednesday, April 28, 2010 6:31 AM
I can get the problem;"Value cannot be null.Parameter name: inputString" .
around the line   return losformatter.Deserialize(Convert.ToBase64String(b));
Comment posted by Justavian on Friday, August 6, 2010 4:05 PM
There are two flaws in the decompress method.  First of all, the "using" statement for the inputMem Memory Stream needs to have a close brace AT THE END of the function.  When the gzip.read line fires, the inputMem stream is already closed.  There's no way to read anything from it at that point!  The second flaw is on the line with "while(byteRead <= 0)".  This tells the system to only continue reading if no bytes were read, which means it does nothing.  This should be "while(byteRead > 0)".

If you change those two lines, everything works.

public static byte[] GzipDecompress(byte[] compData)
{
    GZipStream gzip;
    using (MemoryStream inputMem = new MemoryStream())
    {
        inputMem.Write(compData, 0, compData.Length);
        // Reset the memory stream position to begin decompression.
        inputMem.Position = 0;
        CompressionMode mode = CompressionMode.Decompress;
        gzip = new GZipStream(inputMem, mode, true);

        using (MemoryStream outputMem = new MemoryStream())
        {
            // Read 1024 bytes at a time
            byte[] buf = new byte[1024];
            int byteRead = -1;
            byteRead = gzip.Read(buf, 0, buf.Length);
            while (byteRead > 0)
            {
                //write to memory stream
                outputMem.Write(buf, 0, byteRead);
                byteRead = gzip.Read(buf, 0, buf.Length);
            }
            gzip.Close();
            return outputMem.ToArray();
        }
    }
}
Comment posted by Carol on Friday, August 6, 2010 11:30 PM
Justavian: Thanks for pointing out the typo.

Categories

JOIN OUR COMMUNITY

POPULAR ARTICLES

C# .NET BOOK

C# Book for Building Concepts and Interviews

Tags

JQUERY COOKBOOK

jQuery CookBook