fw: how to compress viewstate
fw: http://www.dotnetbips.com/articles/22d33d11-1a75-42c8-bbf6-ca1a345d3fcf.aspx
Introduction
Developers often worry about performance of their web sites. Every developer wants that his web site be optimized for good performance. There are several factors affecting performance of your web site and one of them is ViewState. In this article I am going to show a way by which you can compress ViewState thus improving the performance.
What is ViewState?
Though this article is not intended to illustrate what ViewState is let's quickly discuss it. If you see HTML source of your web forms you will find a hidden form field named __VIEWSTATE. Clever ASP.NET persists control values in this hidden field helping you to preserve them across post backs. No doubt ViewState is helpful. However, at times ViewState can create performance issues. Since ViewState is transferred between browser and server with each request it adds to network bandwidth.
One way to reduce the ViewState data flowing over the wire is to turn ViewState off. All the web server controls have a property called EnableViewState. Setting this property to false will disable ViewState for that control. However, without ViewState all the job of state persistence comes on your shoulders and can be painful at times. Another option, which the theme of this article, is to compress the ViewState before serializing it on the network. This way the amount of data on the wire will be substantially less.
In ASP.NET 1.x people used this compression technique with custom code because there was no inbuilt way. The .NET 2.0, however, makes your life easy by providing System.IO.Compression namespace. The GZipStream class from System.IO.Compression comes handy in compressing and decompressing streams. Note that GZipStream works with only one stream at a time. The GZipStream class is not specifically designed for ViewState as such but we are going to harness its functionality for compressing and decompressing ViewState.
Creating ViewStateHelper
To begin with, create a new web site using Visual Studio. Then add a class called ViewStateHelper in its App_Code folder. At the top of the class import namespaces as shown below:
using System.IO; using System.IO.Compression;
The System.IO namespace provides stream classes such as MemoryStream. The System.IO.Compression namespace provides GZipStream class that allows you to represent data in gzip format (RFC 1952).
Compressing data
Now add a method called Compress() as shown below:
public static byte[] Compress(byte[] data) { MemoryStream ms = new MemoryStream(); GZipStream stream = new GZipStream (ms, CompressionMode.Compress); stream.Write(data, 0, data.Length); stream.Close(); return ms.ToArray(); }
The Compress() static method accepts a byte array to be compressed and returns compressed byte array. It creates an instance of MemoryStream class. The MemoryStream class represents a stream in memory. It then creates GZipStream object by passing MemoryStream and compression mode of Compress. Instead of MemoryStream you could have passed any type of stream. The compressed data is written to this stream. The Write() method of GZipStream class accepts a byte array, compresses it and writes it to the supplied stream (MEmoryStream in our case). After writing the data GZipStream is closed. Finally, ToArray() method of MemoryStream class converts the underlying stream data into byte array.
Decompressing data
In order to decompress data add another method called Decompress() and key in the following code in it:
public static byte[] Decompress(byte[] data) { MemoryStream ms = new MemoryStream(); ms.Write(data, 0, data.Length); ms.Position = 0; GZipStream stream = new GZipStream(ms, CompressionMode.Decompress); MemoryStream temp = new MemoryStream(); byte[] buffer=new byte[1024]; while (true) { int read = stream.Read(buffer, 0, buffer.Length); if (read <= 0) { break; } else { temp.Write(buffer, 0, buffer.Length); } } stream.Close(); return temp.ToArray(); }
The Decompress() method accepts byte array of compressed data and returns its decompressed version. First, it creates a MemoryStream and writes compressed byte array to it. This stream is then supplied to GZipStream constructor. Notice that this time compression mode is Decompress. The decompressed data also needs to be stored somewhere. The code creates another MemoryStream for this purpose. A while loop reads decompressed data from GZipStream in the chunks of 1024 bytes at a time. This data is then written to the MemoryStream. Finally, GZipStream is closed and contents of decompressed MemoryStream are returned to the caller as byte array.
This completes the ViewStateHelper class. Now you need to put this class in use for compressing and decompressing ViewState.
Creating a web form
Now open the default web form and drag and drop one SQL Data Source control on it. Configure it to select all records from Customers table of Northwind database.
Drag and drop a GridView control and set its DataSourceID property to the ID of SQL Data Source control you just configured. Run the web form so as to ensure that it is working as expected. The following figure shows a sample run of the web form
Customizing ViewState serialization
Most of the times you don't interfere with ViewState serialization and deserialization mechanism at all. However, since you wish to compress the ViewState you need to customize the way ViewState is serialized and deserialized. The Page base class has two virtual methods viz. SavePageStateToPersistenceMedium() and LoadPageStateFromPersistenceMedium(). Together they allow you to customize ViewState serialization and deserialization respectively.
Go in the code behind of the web form and override SavePageStateToPersistenceMedium() method first. You will compress ViewState and store it in a hidden variable in this method. The following code shows this method.
protected override void SavePageStateToPersistenceMedium(object state) { LosFormatter formatter = new LosFormatter(); StringWriter writer = new StringWriter(); formatter.Serialize(writer, state); string viewState = writer.ToString(); byte[] data = Convert.FromBase64String(viewState); byte[] compressedData = ViewStateHelper.Compress(data); string str = Convert.ToBase64String(compressedData); ClientScript.RegisterHiddenField("__MYVIEWSTATE", str); }
The SavePageStateToPersistenceMedium() method accepts an object that represents the ViewState data. Inside it creates LosFormatter object. The LosFormatter class allows you to serialize and deserialize data in ViewState compatible format. The Serialize() method of LosFormatter class accepts a stream where data is to be serialized and the state object. In our example we serialize the data into a StringWriter. The ToString() method of StringWriter returns a string that represents ViewState data in string form. This string data is in Base64 format (this how ASP.NET stores the ViewState)and need to be converted into a plain string. The FromBase64String() method of Convert class does this job. The return value of FromBase64String() method is a byte array which is then compressed using Compress() method of ViewStateHelper class. The compressed byte array is again converted into its Base64 representation using ToBase64String() method of Convert class. Finally, a hidden field named __MYVIEWSTATE is emitted using RegisterHiddenField() method of ClientScript member with its value set to the compressed Base64 string.
Compressing ViewState data is just one side of the coin. Equally important is to decompress it back so that ASP.NET can work with it further. This is done by overriding LoadPageStateFromPersistenceMedium() method as shown below:
protected override object LoadPageStateFromPersistenceMedium() { string viewstate = Request.Form["__MYVIEWSTATE"]; byte[] data = Convert.FromBase64String(viewstate); byte[] uncompressedData = ViewStateHelper.Decompress(data); string str = Convert.ToBase64String (uncompressedData); LosFormatter formatter = new LosFormatter(); return formatter.Deserialize(str); }
The method retrieves __MYVIEWSTATE hidden field from Form collection. This field contains compressed version of the ViewState in Base64 format. In order to decompress it you need to convert it into a byte array first. This is done via FromBase64String() method of Convert class. Then Decompress() method of ViewStateHelper decompresses the data. The decompressed data is again converted into a Base64 string using ToBase64String() method of Convert class. Finally, LosFormatter instance deserializes the decompressed ViewState.
That's it! Your ViewState compression scheme is now ready. During my testing I observed that the uncompressed ViewState was 13,568 bytes whereas the compressed ViewState was 5,932 bytes. A great deal of saving. Isn't it?
Summary
In this article you learnt to improve performance of your web forms by compressing ViewState data. The new GZipStream class offers a ready made way to compress data in gzip format. The compressed ViewState needs to be saved by overriding SavePageStateToPersistenceMedium() method of Page base class. Similarly, LoadPageStateFromPersistenceMedium() needs to be overridden for decompressing and loading the ViewState back.