Search This Blog

Saturday, June 6, 2009

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.IO;
using System.IO.Compression;

///
/// Summary description for ViewStateCompressor
///
public class ViewStateCompressor
{
    public ViewStateCompressor()
    {
        //

        // TODO: Add constructor logic here

        //
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="uncompData"></param>
    /// <returns></returns>
    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();
        }
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="compData"></param>
    /// <returns></returns>
    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 description for CustomPage
///

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));
}

}


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

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.

No comments:

Popular Posts