Me Myself & C#

Manoj Garg’s Tech Bytes – What I learned Today

Posts Tagged ‘PageStatePersister’

ViewState Compression in ASP.NET 2.0

Posted by Manoj Garg on September 16, 2009

STATE as a noun can be described as “the condition of a person or thing, as with respect to circumstances or attributes“.  In ASP.NET world, State of a control or a page is what it looks like, what its value is and what its value was before the page was posted back.

Since HTTP is stateless, to maintain state across postbacks in ASP.NET, some state management techniques are used. In ASP.NET state management can be categorized in two types depending on the location where state is being kept:

  1. Client Side: Client side state management involves keeping the state data on the client end, either on client machine or the browser. There are various mechanisms to maintain state on client side like Cookies, ViewState, Hidden Fields, Control State, Query strings etc.
  2. Server Side: Server side management is keeping state on a different machine be it database or file system on a different computer. Server side state management can be done using Application State, Session state etc. this data can be  stored in the server process or in a database or server file system.

For detailed information about different option available for state management in ASP.NET, please refer this link for MSDN. This link also gives a good comparison of various state management techniques with  their pros n cons.

ViewState is the first thing that come to mind when dealing with postbacks in ASP.NET.

ViewState

In simple terms, ViewState is keeping information about page in a non-readable i.e. serialized way with the page itself. In ASP.NET, view state is kept in a hidden field name _VIEWSTATE on the page. So every time the page loads after the postback this serialized data is read and appropriate state is loaded into the controls.

To understand how viewstate is saved and loaded, one should know the page life cycle i.e. the steps a request, for loading a page, goes through before a page is fully rendered into the browser. This link on MSDN explains Page life cycle along with a good overview of what view state is. This post by Dave Reed gets under the hood of view state and explains the nuances of view state very well. I would recommend reading the above mention post to every one. That post has been the best link I have come across on view state.

As mentioned above, View state is kept in a hidden field _VIEWSTATE on the page. So every time the page is posted back to the server or retrieved from server, the content of View state also travel along with page content. In turn contributing to usage of network resources. Generally, developers have tendency to keep view state enabled for controls on their page. which unknowingly keeps growing the page size. This means as page size grows, it would take longer for the page to completely render itself.

Problem comes when the page size grows inordinately and waiting time for the end user to see the completely rendered page is too high. There are recommendation that one should use viewstate as minimally as possible to ensure the page size is with in limits. But sometime scenario enforces the developer to use viewstate in his/her page. In such cases following are some of the guideline/approaches to keep view state size in check.

  1. Enable view state for a control only when absolutely necessary: Set the EnableViewState attribute for the control to false explicitly, when you are sure that you do not require to keep state for that control.
  2. Don’t save large datasets into view state: Whenever possible keep the datasets and big data tables in cache or say session so that view state is small.
  3. Use IIS Compression: Internet Information Services(IIS) has the feature to provide compression for the pages it serves as response to the requests received. You can configure the IIS on your server to compress the appropriate websites or pages in the website. For detailed information about how to configure the IIS configuration on your server, please read this post by Scott Forsyth.  There is one another post by Shivprasad koirala, which is an extension of the post by Scott Forsyth, it also explains IIS compression in great details along with it also provides some comparison statistics data about what level of compression is good and for what type of pages one should avail this
  4. Using old ASP.NET 1.1 approach for storing the view state on server: In Page life cycle there are two steps available which do the job of fetching the view state from the hidden field and putting the view state content back into the hidden field it was stored. These methods are LoadPageStateFromPersistenceMedium and SavePageStateToPersistenceMedium. These methods are virtual methods from the Page class. A simplest approach would be to store the viewstate in session itself. Following code snippet shows how this could be done by overriding the two above mentioned methods and storing the viewstate in session after serializing and loading the viewstate from session again after desterilizing it. Serialization and deserialization is done using LosFormatter class. One can use any of the available formatters, BinaryFormatter, SOAPFormatter etc., for serializing the viewstate content. Content is stored in session with SessionID being the key.
protected override object LoadPageStateFromPersistenceMedium ()
{
    return (new LosFormatter().Deserialize ((string)Session[Session.SessionID]));
}
protected override void SavePageStateToPersistenceMedium (object state)
{
    LosFormatter los = new LosFormatter();
    los.Serialize (sw, state);
    string vs = sw.ToString ();
    Session[Session.SessionID] = vs;
}

An improvement in above mentioned approach could be to apply the compression on the viewstate content before storing them in session and decompressing the session content before restoring the viewstate. Following code does exactly this. This code introduces one more step, between storing and retrieving the view state from session, by compressing and decompressing the content. This approach again leads to save some more bandwidth.

Following class contains method for compression and decompression. Source code from http://www.dreamincode.net/code/snippet1717.htm

                                                                    

using System.IO;
using System.IO.Compression;

public static class Compressor
{
/// <summary>
/// Method for compressing the ViewState data
/// </summary>
/// <param name="data">ViewState data to compress</param>
/// <returns></returns>
public static byte[] Compress(byte[] data)
{
    //create a new MemoryStream for holding and
    //returning the compressed ViewState
    MemoryStream output = new MemoryStream();
    //create a new GZipStream object for compressing
    //the ViewState
    GZipStream gzip = new GZipStream(output,CompressionMode.Compress, true);
    //write the compressed bytes to the underlying stream
    gzip.Write(data, 0, data.Length);
    //close the object
    gzip.Close();
    //convert the MemoryStream to an array and return
    //it to the calling method
    return output.ToArray();
}

/// <summary>
/// Method for decompressing the ViewState data
/// </summary>
/// <param name="data">Compressed ViewState to decompress</param>
/// <returns></returns>
public static byte[] Decompress(byte[] data)
{
    //create a MemoryStream for holding the incoming data
    MemoryStream input = new MemoryStream();
    //write the incoming bytes to the MemoryStream
    input.Write(data, 0, data.Length);
    //set our position to the start of the Stream
    input.Position = 0;
    //create an instance of the GZipStream to decompress
    //the incoming byte array (the compressed ViewState)
    GZipStream gzip = new GZipStream(input, CompressionMode.Decompress, true);
    //create a new MemoryStream for holding
    //the output
    MemoryStream output = new MemoryStream();
    //create a byte array
    byte[] buff = new byte[64];
    int read = -1;
    //read the decompressed ViewState into
    //our byte array, set that value to our
    //read variable (int data type)
    read = gzip.Read(buff, 0, buff.Length);
    //make sure we have something to read
    while (read > 0)
    {
        //write the decompressed bytes to our
        //out going MemoryStream
        output.Write(buff, 0, read);
        //get the rest of the buffer
        read = gzip.Read(buff, 0, buff.Length);
    }
    gzip.Close();
    //return our out going MemoryStream
    //in an array
    return output.ToArray();
}
}


protected override object LoadPageStateFromPersistenceMedium()

 

 

{

 

 

string viewState = (string)Session[Session.SessionID];

 

 

byte[] bytes = Convert.FromBase64String(viewState);

 

 

bytes = Compressor.Decompress(bytes);

 

 

LosFormatter formatter = new LosFormatter();

 

 

return formatter.Deserialize(Convert.ToBase64String(bytes));

 

 

}

 

protected override void SavePageStateToPersistenceMedium(object state)

 

 

{

 

 

LosFormatter formatter = new LosFormatter();

 

 

StringWriter writer = new StringWriter();

 

 

formatter.Serialize(writer, state);

 

 

string viewStateString = writer.ToString();

 

 

byte[] bytes = Convert.FromBase64String(viewStateString);

 

 

bytes = Compressor.Compress(bytes);

 

 

Session[Session.SessionID] = Convert.ToBase64String(bytes);

}

5. ASP.NET 2.0 approach for saving viewstate on server:

In ASP.NET 2.0, ViewState is saved by a descendant of PageStatePersister class. This class is an abstract class for saving and loading ViewsState and there are two implemented descendants of this class in .Net Framework, named HiddenFieldPageStatePersister and SessionPageStatePersister. By default HiddenFieldPageStatePersister is used to save/load ViewState information, but we can easily get the SessionPageStatePersister to work and save ViewState in Session object. The only thing to do this is to override PageStatePersister property of Page class and ask it to return an instance of SessionPageStatePersister class:

protected override PageStatePersister PageStatePersister

 

{

 

get

 

 

{

 

return new SessionPageStatePersister(Page);

 

}

 

}

 

The PageStatePersister class can be inherited to also created custom storage mediums for session state. The PageStatePersister class has two methods Load() and Save() which can be used to provide custom loading and saving media for ViewState.

public class StreamPageStatePersister : PageStatePersister

 

 

{

 

 

public StreamPageStatePersister(Page page)

 

: base(page)

 

{

 

}

 

public override void Load()

 

{

 

Stream stateStream = GetSecureStream();

 

// Read the state string, using the StateFormatter.

 

 

StreamReader reader = new StreamReader(stateStream);

 

IStateFormatter formatter = this.StateFormatter;

 

string fileContents = reader.ReadToEnd();

 

// Deserilize returns the Pair object that is serialized in

 

 

// the Save method.

 

 

Pair statePair = (Pair)formatter.Deserialize(fileContents);

 

ViewState = statePair.First;

 

ControlState = statePair.Second;

 

reader.Close();

 

stateStream.Close();

 

}

 

 

 

public override void Save()

 

{

 

 

if (ViewState != null || ControlState != null)

 

{

 

if (Page.Session != null)

 

{

 

Stream stateStream = GetSecureStream();

 

 

StreamWriter writer = new StreamWriter(stateStream);

 

 

IStateFormatter formatter = this.StateFormatter;

 

Pair statePair = new Pair(ViewState, ControlState);

 

 

// Serialize the statePair object to a string.

 

 

string serializedState = formatter.Serialize(statePair);

 

 

writer.Write(serializedState);

 

writer.Close();

 

stateStream.Close();

 

}

 

else

 

 

throw new InvalidOperationException(“Session needed for StreamPageStatePersister.”);

 

}

 

}

 

}

 

Then a PageAdapter can be used to use this persister

public class MyPageAdapter : System.Web.UI.Adapters.PageAdapter

 

{

 

public override PageStatePersister GetStatePersister() {

 

return new Samples.AspNet.CS.StreamPageStatePersister(Page);

 

}

 

}

 

 

This adapter can be configured via a .browser file placed in App_Browsers folder locally with the web site directory.

<browsers>
    <browser refid="Default" >
        <controlAdapters>
            <adapter
                controlType="System.Web.UI.Page"
                adapterType="MyPageAdapter" />
        </controlAdapters>
    </browser>
</browsers>

 

This all above content was related to removing/reducing the Viewstate getting transferred over network. But there is still one more type of state “ControlState”. In layman’s terms, Its the minimal information that a control needs to render and function itself properly after a postback. A more apt definition from MSDN is:

Control state, introduced in ASP.NET version 2.0, is similar to view state but functionally independent of view state. A page developer can disable view state for the page or for an individual control for performance. However, control state cannot be disabled. Control state is designed for storing a control’s essential data (such as a pager control’s page number) that must be available on postback to enable the control to function even when view state has been disabled. By default, the ASP.NET page framework stores control state in the page in the same hidden element in which it stores view state. Even if view state is disabled, or when state is managed using Session, control state travels to the client and back to the server in the page. On postback, ASP.NET deserializes the contents of the hidden element and loads control state into each control that is registered for control state.

As it is mentioned in the above description, that ControlState will be traveling with the page all the time. There may come a requirement where we want to have it stored somewhere else other then the page hidden fields. While searching for methods to reduce the viewstate size, i stumbled across this post by SZOKELIZER, which describes a way to store the ControlState in session as well. ASP.NET provides a switch “RequiresControlStateInSession ” which allows to store the control state in session rather then the page itself. So for the sake of completeness I am putting the code that needs to be put to enable storing the control state in session from the above mentioned post.

<system.web>
    <browserCaps>
      <case>
        RequiresControlStateInSession=true
      </case>
    </browserCaps>
  </system.web>

 

 

Conclusion

In this post I have written about different mechanisms to reduce size of viewstate that travels along with an asp/aspx page every time. I have tried to covered few of the approaches that can be used but I am sure there can be many more. One such approach suggested by a friend of mine is using HttpHandlers to cut the viewstate from the request and restore it back. I am working on a poc for this. I will post it once it is complete. Please do post a comment, if you have come across any other way to tackle this or if there is some correction in approaches I have described above.

Posted in .Net 2.0, ASP.Net, C#, ViewState | Tagged: , , , , , , , , , , | 3 Comments »