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.

How ASP.NET Web Pages are Processed on the Web Server

.
Introduction

Have you ever wondered, precisely, what happens on the Web server when a request for an ASP.NET Web page comes in?
How does the Web server handle the incoming request?
How is the HTML that is emitted to the client generated?
What mechanisms are possible for us developers to work with an incoming request in its various stages of processing?

In this article we'll take a rather detailed look at how ASP.NET Web pages are processed on the Web server.


Step 0: The Browser Makes an HTTP Request for an ASP.NET Web PageThe entire process begins with a Web browser making a request for an ASP.NET Web page. For example, a user might type into their browser's Address window the URL for this article, http://aspnet.4guysfromrolla.com/articles/011404.aspx. The Web browser, then, would make an HTTP request to the 4Guys Web server, asking for the particular file /articles/011404-1.aspx.

Step 1: The Web Server Receives the HTTP RequestThe sole task of a Web server is to accept incoming HTTP requests and to return the requested resource in an HTTP response. The 4Guys Web server runs Microsoft's Internet Information Services (IIS) Web server. The first things IIS does when a request comes in is decide how to handle the request. Its decision is based upon the requested file's extension. For example, if the requested file has the .asp extension, IIS will route the request to be handled by asp.dll.
There are numerous file extensions that map to the ASP.NET engine, some of which include:
.aspx, for ASP.NET Web pages,
.asmx, for ASP.NET Web services,
.config, for ASP.NET configuration files,
.ashx, for custom ASP.NET HTTP handlers,
.rem, for remoting resources,
And others!
In the IIS administration screens, you can configure the extension mappings. The screenshot to the right shows the configuration screen for IIS 5.0. You could, for example, add your own custom extensions here. That is, you could have requests for .scott files routed to the ASP.NET engine.
The diagram below illustrates the steps 0 and 1 of a request for an ASP.NET Web page. When a request comes into the Web server, it is routed to the proper place (perhaps asp.dll for classic ASP page requests, perhaps the ASP.NET engine for ASP.NET requests) based on the requested file's extension.











Step 2: Examining the ASP.NET EngineAn initial request for

http://aspnet.4guysfromrolla.com/articles/011404.aspx will reach IIS and then be routed to the ASP.NET engine, but what happens next? The ASP.NET engine is often referred to as the ASP.NET HTTP pipeline, because the incoming request passes through a variable number of HTTP modules on its way to an HTTP handler.
HTTP modules are classes that have access to the incoming request. These modules can inspect the incoming request and make decisions that affect the internal flow of the request. After passing through the specified HTTP modules, the request reaches an HTTP handler, whose job it is to generate the output that will be sent back to the requesting browser. The following diagram illustrates the pipeline an ASP.NET request flows through.

















There are a number of pre-built HTTP modules that are included in the HTTP pipeline by default. These modules include:
OutputCache, which handles returning and caching the page's HTML output, if needed
Session, which loads in the session state based on the user's incoming request and the session method specified in the Web.config file
FormsAuthentication, which attempts to authenticate the user based on the forms authentication scheme, if used
And others!
In fact, you can see a precise list of what modules are used by default by going to the machine.config file (located in the $WINDOWS$\Microsoft.NET\Framework\$VERSION$\CONFIG directory) and searching for the element. The following shows the default element of httpModules:














HTTP handlers are the endpoints in the ASP.NET HTTP pipeline. The job of the HTTP handler is to generate the output for the requested resource. For ASP.NET Web pages, this means rendering the Web controls into HTML and returning this HTML. For a Web service, it would involve executing the specified method and wrapping its return values into an appropriately formatted SOAP response. (For more on Web services, be sure to read: An Extensive Examination of Web Services.)
Different ASP.NET resources use different HTTP handlers. The handlers used by default are spelled out in the machine.config's section. Entries in this section refer to classes that are either HTTP handlers themselves or are HTTP handler factories. An HTTP handler factory merely returns a suitable HTTP handler instance when invoked.
The following shows a snippet of the httpHandler element in the default machine.config file:









Realize that you can create your own HTTP modules and HTTP handlers, and then plug them into the pipeline for all Web sites on the Web server by modifying machine.config, or you can add them to a particular Web application by modifying that application's Web.config file. A thorough discussion on using HTTP modules and handlers is far beyond the scope of this article, but realize that you can accomplish some neat things using modules and handlers. For example, you can use HTTP modules to provide a custom URL rewritter, which can be useful for automatically fixing 404 errors to using shorter and user-friendlier URLs. (See Rewrite.NET - A URL Rewriting Engine for .NET for an example of using HTTP modules for URL rewriting.) Another cool use of modules is compressing the generated HTML.
For a good discussion on HTTP modules and handlers be sure to read Mansoor Ahmed Siddiqui's article: HTTP Handlers and HTTP Modules in ASP.NET.
Step 3: Generating the OutputThe final step is for the suitable HTTP handler to generate the appropriate output. This output, then, is passed back through the HTTP modules and then back to IIS, which then sends it back to the client that initiated the request. (If the client was a Web browser, the Web browser would receive this HTML and display it.)
Since the steps for generating the output differ by HTTP handler, let's focus in on one in particular - the HTTP handler that is used to render ASP.NET Web pages. To retrace the initial steps, when a request comes into IIS for an ASP.NET page (i.e., one with a .aspx extension), the request is handed off to the ASP.NET engine. The request then moves through the modules. The request is then routed to the PageHandlerFactory, since in the machine.config's section we have the mapping:


The PageHandlerFactory class is an HTTP handler factory. It's job is to provide an instance of an HTTP handler that can handle the request. What PageHandlerFactory does is find the compiled class that represents the ASP.NET Web page that is being requested.
If you use Visual Studio .NET to create your ASP.NET Web pages you know that the Web pages are composed of two separate files: a .aspx file, which contains just the HTML markup and Web controls, and a .aspx.vb or .aspx.cs file that contains the code-behind class (which contains the server-side code). If you don't use Visual Studio .NET, you likely use a server-side

"script" block to hold the server-side code. Regardless of what approach you use, when the ASP.NET Web page is first visited after a change to the HTML or Web control content, the ASP.NET engine creates a class that derives from the System.Web.UI.Page class. This dynamically created class is then compiled.



Not coincidentally, the Page class implements IHttpHandler, thereby indicating that it suffices as an HTTP handler. What the PageHandlerFactory does, then, is check to see if a compiled version of the requested ASP.NET Web page's class exists. If not, it dynamically creates this class and compiles it. This class, then, has a particular method invoked which generates the page's complete HTML markup. It's this HTML markup that is then returned to the client. (Have you noticed that when visiting an ASP.NET Web page after making changes to the HTML or Web control content, there is a bit of a delay? This delay is due to the ASP.NET engine needing to recreate and recompile the ASP.NET page's corresponding class.)





Realize that the 011404-1.aspx class might need to first be generated and compiled by the PageHandlerFactory before it can be invoked to generate the HTML. The process of page rendering - which involves obtaining the HTML markup for the requested ASP.NET Web page - is a bit beyond the scope of this article, but is discussed in good detail in Dino Esposito's article, The ASP.NET Page Object Model.

Conclusion

In this article we looked at an overview of how ASP.NET Web pages are processed on the Web server. It involves a number of steps, starting with a request from a client, and culminating with the requested ASP.NET Web page's class generating the page's rendered HTML. In between, the request bounces through IIS and into the ASP.NET engine, where it proceeds through a module pipeline down into the corresponding HTTP handler.

Popular Posts