Recipe 16.1 Reducing Page Size by Selectively Disabling the ViewState

     

Recipe 16.1 Reducing Page Size by Selectively Disabling the ViewState

16.1.1 Problem

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

16.1.2 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


16.1.3 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:

 
figs/vbicon.gif
 Page.EnableViewState = False 
figs/csharpicon.gif
 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:

 
figs/vbicon.gif
 pageHeader.EnableViewState = False 
figs/csharpicon.gif
 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:

 
figs/vbicon.gif
 For Each item In dgBooks.Items item.EnableViewState = False Next 
figs/csharpicon.gif
 foreach (DataGridItem item in dgBooks.Items) { item.EnableViewState = false; } 

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.

16.1.4 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 } 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2006
Pages: 179

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net