16.1. Reducing Page Size by Selectively Disabling the ViewState

Problem

You want to reduce the size of your application pages to improve performance.

Solution

Review each page of your application and each of its controls to determine if the ViewState is required. Disable the ViewState where it is not explicitly needed.

In the code-behind class for the page, use the .NET language of your choice to do either of the following:

  • Disable the ViewState for the page by setting Page.EnableViewState to False.

  • Disable the ViewState for individual controls by setting the control’s EnableViewState property to False.

To illustrate these performance improvements, we took two examples from Chapter 1 and optimized them by disabling the ViewState. In the first example, we took the ASP.NET page created for Recipe 1.19, which displays a grid containing books and price data, and disabled the ViewState at the page level. Table 16-1 shows the page and ViewState size before and after the optimization.

Table 16-1. ViewState performance improvement for Recipe 1.19 example

Before optimization

After optimization

Page size

18,175 bytes

11,271 bytes

ViewState size

6,953 bytes

49 bytes

In the second example, we have used the ASP.NET page created in Recipe 1.12, replaced the table used for the page header with the header user control created in Recipe 4.1, and then disabled the ViewState for the header control as well as the row controls within the DataGrid that appears within the page body. Example 16-1 shows the .aspx file for this application. The code-behind class for the application is shown in Example 16-2 (VB) and Example 16-3 (C#). Table 16-2 shows the page and ViewState sizes before and after optimization.

Table 16-2. ViewState performance improvement for Recipe 1.12 example

Before optimization

After optimization

Page size

14,643 bytes

9,251 bytes

ViewState size

6,665 bytes

1,273 bytes

Discussion

The ViewState is used to keep track of the state of each control on a page and to rehydrate the control upon postback to the server. Because of its ability to maintain state when a page is posted back to the server, the use of the ViewState significantly reduces the amount of code you would otherwise have to write. Thanks to the ViewState, you no longer need to extract values from the posted form for processing or reset the control values when you display the page again, as was the case with classic ASP. The controls are simply accessed as they were when the page was initially generated.

While use of the ViewState significantly reduces your coding and maintenance efforts, it comes at a cost. All of the data required to keep track of the control’s state is stored in a hidden input control in the HTML page, as shown next. Depending on the number and types of controls you use on your pages, the ViewState can get very large, resulting in a significant decrease in performance. Because the ViewState data is sent to the browser when the page is rendered and returned to the server as part of the post-back, a performance hit occurs when the page is first displayed as well as when the page is posted back to the server. Performance is degraded not so much by the generation of the ViewState data itself when the page is first rendered, but rather by the transfer of the extra ViewState data to and from the browser on post-backs, as well as by the processing of the data by the browser. Here is a typical ViewState input control:

<input type="hidden" name="_  _VIEWSTATE" 
       value="dDwtOTQzNjg3NDE1O3Q8O2w8aTwxPjs"/>

While “byte counters” will be quick to completely disable the ViewState because of its inevitable negative impact on performance, there is a compromise available that provides the best of both worlds: selectively disable ViewState because it is not needed for all pages or controls. By reviewing each of the pages in your application, you can significantly improve the performance of the application without losing the benefits of the ViewState.

The first step when reviewing a page is to determine if the page does a postback to itself. If not, then the ViewState can be disabled for the entire page. This is done by placing this line of code in the Page_Load method:

               Discussion
  Page.EnableViewState = False

Discussion
  Page.EnableViewState = False;

Even with the ViewState disabled for a page, a few bytes will remain in the value setting of the hidden input control. If you are absolutely determined to remove all traces of the ViewState, you must either remove the form element or remove the runat="server" attribute from the form element. Either action can cause maintenance issues later and the resulting savings of less than 50 bytes in a 20K page has no measurable performance impact, so we do not recommend such an extreme remedy.

If the page does a postback to itself, you will need to review each of the controls on the page. For each control you need to determine whether any state information is required by the control upon postback. If no state information is required, the ViewState for the control can be disabled.

The example page created in Recipe 1.19 displays a grid containing books and price data. The page contains two “action” controls that are anchors used to access other pages; therefore, this page has no mechanism to postback to itself and is a good candidate for disabling the ViewState at the page level, a conclusion borne out by the results shown in Table 16-1. After this optimization, the page size is 62% of the original size and the ViewState represents less than 0.5% of the optimized page.

The example page created in Recipe 1.12 is also a good candidate for performance improvement. As mentioned, we replaced the table used for the page header at the top of the page with the header user control created in Recipe 4.1. This change better illustrates our point and is shown in the .aspx file in Example 16-1. This page is similar to the page created in Recipe 1.19 but has three additional “action” controls used to sort the data in the grid. Clicking on the column headers in the grid causes the page to be posted back to itself with the data sorted by the column clicked; therefore, this page cannot have the ViewState disabled at the page level.

Because the ViewState cannot be disabled at the page level, we need to instead review each control to determine if the ViewState is needed. The page contains two controls, a header control and a DataGrid control. The header control contains no “action” controls and no programmatically set content; therefore, the ViewState for the header control can be disabled using the code shown here:

               Discussion
  pageHeader.EnableViewState = False

Discussion
  pageHeader.EnableViewState = false;

While you might be tempted to disable the ViewState for the DataGrid, because all of the data is regenerated on each postback, you cannot. ASP.NET needs the ViewState information for the controls within the header of the DataGrid to process its click events and to execute the dgBooks_SortCommand method. If you disable the ViewState for the DataGrid, the postback will occur but none of the event handlers will be called.

A DataGrid is itself a container of controls. At its highest level, a DataGrid consists of a header control and one or more row controls. In this example, only the header contains “action” controls and because the data in each row is regenerated with each postback, the ViewState for the row controls can be disabled using the code shown here:

               Discussion
  For Each item In dgBooks.Items
    item.EnableViewState = False
  Next

Discussion
  foreach (DataGridItem item in dgBooks.Items)
  {
    item.EnableViewState = false;
  }

Tip

When programmatically disabling the ViewState of individual controls, the code that performs the disabling must be executed anytime the page is rendered. In addition, the disabling of controls within a DataGrid must be performed after data binding.

The results in Table 16-2 confirm the advantage of this optimization. By disabling the ViewState for the page header and for each row in the DataGrid, we have significantly reduced the size of the ViewState and the overall page size as well. After optimization, the page size is 63% of the original size and the ViewState represents less than 14% of the optimized page.

See Also

Recipe 1.12; Recipe 1.19; Recipe 4.1

Example 16-1. Modified .aspx file from Recipe 1.12

<%@ Page Language="vb" AutoEventWireup="false"
         Codebehind="CH16ViewStatePerformanceVB2.aspx.vb" 
         Inherits="ASPNetCookbook.VBExamples.CH16ViewStatePerformanceVB2"%>
<%@ Register TagPrefix="ASPCookbook" TagName="PageHeader" 
                               Src="CH04UserControlHeaderVB.ascx" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>View State Performance</title>
    <link rel="stylesheet" href="css/ASPNetCookbook.css">
  </head>
  <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0">
    <form id="frmDatagrid" method="post" runat="server">
      <ASPCookbook:PageHeader id="Pageheader" runat="server" />
      <table width="90%" align="center" border="0">
        <tr>
          <td><img src="images/spacer.gif" height="10" border="0"></td>
        </tr>
        <tr>
          <td align="center" class="PageHeading">
            Improving ViewState Performance of Recipe 1-12 (VB)
          </td>
        </tr>
        <tr>
          <td><img src="images/spacer.gif" height="10" border="0"></td>
        </tr>
        <tr>
          <td align="center">
            <asp:datagrid 
              id="dgBooks" 
              runat="server" 
              bordercolor="000080" 
              borderwidth="2px"
              autogeneratecolumns="False"
              width="100%"
              allowsorting="True">

              <headerstyle 
                horizontalalign="Center" 
                forecolor="#FFFFFF" 
                backcolor="#000080" 
                font-bold=true
                cssclass="TableHeader" /> 

              <itemstyle
                backcolor="#FFFFE0" 
                cssclass="TableCellNormal" />

              <alternatingitemstyle 
                backcolor="#FFFFFF" 
                cssclass="TableCellAlternating" />
              
              <columns>
                <asp:boundcolumn datafield="Title" 
                                 sortexpression="Title" />
                <asp:boundcolumn datafield="ISBN" 
                                 itemstyle-horizontalalign="Center" 
                                 sortexpression="ISBN" />
                <asp:boundcolumn datafield="Publisher" 
                                 itemstyle-horizontalalign="Center" 
                                 sortexpression="Publisher" />
              </columns>
            </asp:datagrid>
          </td>
        </tr>
      </table>
    </form>
  </body>
</html>

Example 16-2. Optimized code-behind forRecipe 1.12 (.vb)

Option Explicit On 
Option Strict On
'-----------------------------------------------------------------------------
'
'   Module Name: CH16ViewStatePerformanceVB2.aspx.vb
'
'   Description: This module provides the code behind for the 
'                CH16ViewStatePerformanceVB2.aspx page
'
'*****************************************************************************
Imports Microsoft.VisualBasic
Imports System.Configuration
Imports System.Data
Imports System.Data.OleDb
Imports System.Web.UI.WebControls

Namespace ASPNetCookbook.VBExamples
  Public Class CH16ViewStatePerformanceVB2
    Inherits System.Web.UI.Page

    'controls on the form
    Protected WithEvents dgBooks As System.Web.UI.WebControls.DataGrid
                      Protected pageHeader As ASPNetCookbook.VBExamples.CH04UserControlHeaderVB

    'the following enumeration is used to define the sort orders
    Private Enum enuSortOrder
      soAscending = 0
      soDescending = 1
    End Enum

    'strings to use for the sort expressions and column title
    'separate arrays are used to support the sort expression and titles
    'being different
    Private ReadOnly sortExpression( ) As String = {"Title", "ISBN", "Publisher"}
    Private ReadOnly columnTitle( ) As String = {"Title", "ISBN", "Publisher"}

    'the names of the variables placed in the viewstate
    Private Const VS_CURRENT_SORT_EXPRESSION As String = "currentSortExpression"
    Private Const VS_CURRENT_SORT_ORDER As String = "currentSortOrder"

    '*************************************************************************
    '
    '   ROUTINE: Page_Load
    '
    '   DESCRIPTION: This routine provides the event handler for the page load
    '                event.  It is responsible for initializing the controls 
    '                on the page.
    '-------------------------------------------------------------------------
    Private Sub Page_Load(ByVal sender As System.Object, _
                          ByVal e As System.EventArgs) _
            Handles MyBase.Load

      Dim defaultSortExpression As String
      Dim defaultSortOrder As enuSortOrder

      If (Not Page.IsPostBack) Then
        'sort by title, ascending as the default
        defaultSortExpression = sortExpression(0)
        defaultSortOrder = enuSortOrder.soAscending

        'store current sort expression and order in the viewstate then
        'bind data to the DataGrid
        viewstate(VS_CURRENT_SORT_EXPRESSION) = defaultSortExpression
        viewState(VS_CURRENT_SORT_ORDER) = defaultSortOrder
        bindData(defaultSortExpression, _
                 defaultSortOrder)

        'disable the ViewState for controls that do not need it
                          disableViewState( )
      End If
    End Sub  'Page_Load

    '*************************************************************************
    '
    '   ROUTINE: dgBooks_SortCommand
    '
    '   DESCRIPTION: This routine provides the event handler for the datagrid
    '                sort event.  It is responsible re-binding the data to the
    '                datagrid by the selected column.
    '-------------------------------------------------------------------------
    Private Sub dgBooks_SortCommand(ByVal source As Object, _
                                    ByVal e As DataGridSortCommandEventArgs) _
            Handles dgBooks.SortCommand

      Dim newSortExpression As String
      Dim currentSortExpression As String
      Dim currentSortOrder As enuSortOrder

      'get the current sort expression and order from the viewstate
      currentSortExpression = CStr(viewstate(VS_CURRENT_SORT_EXPRESSION))
      currentSortOrder = CType(viewstate(VS_CURRENT_SORT_ORDER), enuSortOrder)

      'check to see if this is a new column or the sort order
      'of the current column needs to be changed.
      newSortExpression = e.SortExpression
      If (newSortExpression = currentSortExpression) Then
        'sort column is the same so change the sort order
        If (currentSortOrder = enuSortOrder.soAscending) Then
          currentSortOrder = enuSortOrder.soDescending
        Else
          currentSortOrder = enuSortOrder.soAscending
        End If
      Else
        'sort column is different so set the new column with ascending
        'sort order
        currentSortExpression = newSortExpression
        currentSortOrder = enuSortOrder.soAscending
      End If

      'update the view state with the new sort information
      viewstate(VS_CURRENT_SORT_EXPRESSION) = currentSortExpression
      viewstate(VS_CURRENT_SORT_ORDER) = currentSortOrder

      'rebind the data in the datagrid
      bindData(currentSortExpression, _
               currentSortOrder)

      'disable the ViewState for controls that do not need it
                        disableViewState( )
    End Sub  'dgBooks_SortCommand

    '*************************************************************************
    '
    '   ROUTINE: bindData
    '
    '   DESCRIPTION: This routine queries the database for the data to 
    '                displayed and binds it to the datagrid
    '-------------------------------------------------------------------------
    Private Sub bindData(ByVal sortExpression As String, _
                         ByVal sortOrder As enuSortOrder)
      Dim dbConn As OleDbConnection
      Dim da As OleDbDataAdapter
      Dim ds As DataSet
      Dim strConnection As String
      Dim strSQL As String
      Dim index As Integer
      Dim col As DataGridColumn
      Dim colImage As String
      Dim strSortOrder As String

      Try
        'get the connection string from web.config and open a connection 
        'to the database
        strConnection = _
            ConfigurationSettings.AppSettings("dbConnectionString")
        dbConn = New OleDbConnection(strConnection)
        dbConn.Open( )

        'build the query string and get the data from the database
        If (sortOrder = enuSortOrder.soAscending) Then
          strSortOrder = " ASC"
        Else
          strSortOrder = " DESC"
        End If

        strSQL = "SELECT Title, ISBN, Publisher " & _
                 "FROM Book " & _
                 "ORDER BY " & sortExpression & _
                 strSortOrder

        da = New OleDbDataAdapter(strSQL, dbConn)
        ds = New DataSet
        da.Fill(ds)

        'loop through the columns in the datagrid updating the heading to 
        'mark which column is the sort column and the sort order
        For index = 0 To dgBooks.Columns.Count - 1
          col = dgBooks.Columns(index)

          'check to see if this is the sort column
          If (col.SortExpression = sortExpression) Then
            'this is the sort column so determine whether the ascending or
            'descending image needs to be included
            If (sortOrder = enuSortOrder.soAscending) Then
              colImage = " <img src='images/sort_ascending.gif' border='0'>"
            Else
              colImage = " <img src='images/sort_descending.gif' border='0'>"
            End If
          Else
            'This is not the sort column so include no image html
            colImage = ""
          End If  'If (col.SortExpression = sortExpression)

          'set the title for the column
          col.HeaderText = columnTitle(index) & colImage
        Next index

        'set the source of the data for the datagrid control and bind it
        dgBooks.DataSource = ds
        dgBooks.DataBind( )

      Finally
        'cleanup
        If (Not IsNothing(dbConn)) Then
          dbConn.Close( )
        End If
      End Try
    End Sub  'bindData

    '*************************************************************************
    '
    '   ROUTINE: disableViewState
    '
    '   DESCRIPTION: This routine disables the ViewState for all controls 
    '                on the page that do not need to use it.
    '-------------------------------------------------------------------------
    Private Sub disableViewState( )
                        Dim item As DataGridItem

                        'disable the ViewState for the page header
                        pageHeader.EnableViewState = False

                        'disable the ViewState for each row in the DataGrid
                        For Each item In dgBooks.Items
                          item.EnableViewState = False
                        Next item
                      End Sub  'disableViewState
  End Class  'CH16ViewStatePerformanceVB2
End Namespace

Example 16-3. Optimized code-behind for Recipe 1.12 (.cs)

//----------------------------------------------------------------------------
//
//   Module Name: CH16ViewStatePerformanceCS2.aspx.cs
//
//   Description: This class provides the code behind for
//                CH16ViewStatePerformanceCS2.aspx
//
//****************************************************************************
using System;
using System.Configuration;
using System.Data;
using System.Data.OleDb;
using System.Web.UI.WebControls;

namespace ASPNetCookbook.CSExamples
{
  public class CH16ViewStatePerformanceCS2 : System.Web.UI.Page
  {
    // controls on the form
    protected System.Web.UI.WebControls.DataGrid dgBooks;
                      protected ASPNetCookbook.CSExamples.CH04UserControlHeaderCS pageHeader;

    // the following enumeration is used to define the sort orders
    private enum enuSortOrder : int
    {
      soAscending = 0,
      soDescending = 1
    }

    // strings to use for the sort expressions and column title
    // separate arrays are used to support the sort expression and titles
    // being different
    static readonly String [] sortExpression =
                                new String [] {"Title", "ISBN", "Publisher"};
    static readonly String[] columnTitle =
                                new String [] {"Title", "ISBN", "Publisher"};

    // the names of the variables placed in the viewstate
    static readonly String VS_CURRENT_SORT_EXPRESSION = 
                                                      "currentSortExpression";
    static readonly String VS_CURRENT_SORT_ORDER = "currentSortOrder";

    //************************************************************************
    //
    //   ROUTINE: Page_Load
    //
    //   DESCRIPTION: This routine provides the event handler for the page
    //                load event.  It is responsible for initializing the
    //                controls on the page.
    //------------------------------------------------------------------------
    private void Page_Load(object sender, System.EventArgs e)
    {
      String defaultSortExpression;
      enuSortOrder defaultSortOrder;

      // wire the event handler for the sort command
      this.dgBooks.SortCommand +=
        new DataGridSortCommandEventHandler(this.dgBooks_SortCommand);

      if (!Page.IsPostBack)
      {
        // sort by title, ascending as the default
        defaultSortExpression = sortExpression[0];
        defaultSortOrder = enuSortOrder.soAscending;

        // bind data to the DataGrid
        this.ViewState.Add(VS_CURRENT_SORT_EXPRESSION, defaultSortExpression);
        this.ViewState.Add(VS_CURRENT_SORT_ORDER, defaultSortOrder);
        bindData(defaultSortExpression,
                 defaultSortOrder);

        // disable the ViewState for controls that do not need it
                          disableViewState( );
      }
    }  // Page_Load

    //************************************************************************
    //
    //   ROUTINE: dgBooks_SortCommand
    //
    //   DESCRIPTION: This routine provides the event handler for the
    //                datagrid sort event.  It is responsible re-binding
    //                the data to the datagrid by the selected column.
    //------------------------------------------------------------------------
    private void dgBooks_SortCommand(Object source,
      System.Web.UI.WebControls.DataGridSortCommandEventArgs e)
    {
      String newSortExpression = null;
      String currentSortExpression = null;
      enuSortOrder currentSortOrder;

      // get the current sort expression and order from the viewstate
      currentSortExpression =
                        (String)(this.ViewState[VS_CURRENT_SORT_EXPRESSION]);
      currentSortOrder =
                        (enuSortOrder)(this.ViewState[VS_CURRENT_SORT_ORDER]);

      // check to see if this is a new column or the sort order
      // of the current column needs to be changed.
      newSortExpression = e.SortExpression;
      if (newSortExpression == currentSortExpression)
      {
        // sort column is the same so change the sort order
        if (currentSortOrder == enuSortOrder.soAscending)
        {
          currentSortOrder = enuSortOrder.soDescending;
        }
        else
        {
          currentSortOrder = enuSortOrder.soAscending;
        }
      }
      else
      {
        // sort column is different so set the new column with ascending
        // sort order
        currentSortExpression = newSortExpression;
        currentSortOrder = enuSortOrder.soAscending;
      }

      // update the view state with the new sort information
      this.ViewState.Add(VS_CURRENT_SORT_EXPRESSION, currentSortExpression);
      this.ViewState.Add(VS_CURRENT_SORT_ORDER, currentSortOrder);

      // rebind the data in the datagrid
      bindData(currentSortExpression,
               currentSortOrder);

      // disable the ViewState for controls that do not need it
                        disableViewState( );
    }  // dgBooks_SortCommand

    //************************************************************************
    //
    //   ROUTINE: bindData
    //
    //   DESCRIPTION: This routine queries the database for the data to
    //                displayed and binds it to the repeater
    //------------------------------------------------------------------------
    private void bindData(String sortExpression,
      enuSortOrder sortOrder)
    {
      OleDbConnection dbConn = null;
      OleDbDataAdapter da = null;
      DataSet ds = null;
      String strConnection = null;
      String strSQL =null;
      int index = 0;
      DataGridColumn col = null;
      String colImage = null;
      String strSortOrder = null;

      try
      {
        // get the connection string from web.config and open a connection 
        // to the database
        strConnection = 
          ConfigurationSettings.AppSettings["dbConnectionString"];
        dbConn = new OleDbConnection(strConnection);
        dbConn.Open( );

        // build the query string and get the data from the database
        if (sortOrder == enuSortOrder.soAscending)
        {
          strSortOrder = " ASC";
        }
        else
        {
          strSortOrder = " DESC";
        }

        strSQL = "SELECT Title, ISBN, Publisher " +
                 "FROM Book " +
                 "ORDER BY " + sortExpression +
                 strSortOrder;

        da = new OleDbDataAdapter(strSQL, dbConn);
        ds = new DataSet( );
        da.Fill(ds, "Table");

        // loop through the columns in the datagrid updating the heading to
        // mark which column is the sort column and the sort order
        for (index = 0; index < dgBooks.Columns.Count; index++)
        {
          col = dgBooks.Columns[index];
          // check to see if this is the sort column
          if (col.SortExpression == sortExpression)
          {
            // this is the sort column so determine whether the ascending or
            // descending image needs to be included
            if (sortOrder == enuSortOrder.soAscending)
            {
              colImage = " <img src='images/sort_ascending.gif' border='0'>";
            }
            else
            {
              colImage = " <img src='images/sort_descending.gif' border='0'>";
            }
          }
          else
          {
            // This is not the sort column so include no image html
            colImage = "";
          }  // if (col.SortExpression == sortExpression)

          // set the title for the column
          col.HeaderText = columnTitle[index] + colImage;
        }  // for index

        // set the source of the data for the datagrid control and bind it
        dgBooks.DataSource = ds;
        dgBooks.DataBind( );
      }  // try

      finally
      {
        //clean up
        if (dbConn != null)
        {
          dbConn.Close( );
        }
      }  // finally
    }  // bindData

    //************************************************************************
    //
    //   ROUTINE: disableViewState
    //
    //   DESCRIPTION: This routine disables the ViewState for all controls 
    //                on the page that do not need to use it.
    //------------------------------------------------------------------------
    private void disableViewState( )
                      {
                        // disable the ViewState for the header
                        pageHeader.EnableViewState = false;

                        // disable the ViewState for each row in the DataGrid
                        foreach (DataGridItem item in dgBooks.Items)
                        {
                          item.EnableViewState = false;
                        }
                      }  // disableViewState
  }  // CH16ViewStatePerformanceCS2
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset