Introduction
ASP.NET ViewState is a great mechanism that simplifies the life of ASP.NET developers. But, as everybody knows, the .NET Framework saves the ViewState data as a hidden field on your ASPX page. If your page has only a few controls, this is not a problem. But, if your page has some Panel
s and/or some DataGrid
s, with the technique demonstrated on this article, you could reduce dramatically the load time of the page.
We will start our analysis thinking where the Framework needs to work with the data saved on the ViewState hidden field. The answer is: only on the server side. The system doesn't need to work with the ViewState data on the client side, so, if we start to save this data on the server instead of carrying all this from the server to the client and from the client to the server again, we will save a lot of time while loading our documents. You probably won't note the difference while loading the page on the same computer that the application is installed, but, test it using a dial-up connection and you will see what happens. You cold also see the the size of the source code generated with and without this technique.
This example consist of a class that inherits the System.Web.UI.Page
, and overrides the methods SavePageStateToPersistenceMedium
and LoadPageStateFromPersistenceMedium
. These methods are responsible for saving and loading the ViewState used by the controls on a page. What we do is intercept the call to these methods and, using some simple custom config keys on the Web.Config, set how this class should work. It can save the ViewState on the server using two destinations: Session
and Cache
, and, has the ability to work as the original class, saving the ViewState data to the ASPX page.
If you already have a project and want to start using this technique, all that you need to do is add a reference to the class that accompanies this example and inherit your "code-behind files" from it instead of the System.Web.UI.Page
, and add the Config information to your Web.Config.
To develop this technique, I read a lot of articles over the internet, but, one really "opened my mind" of how this implementation could be done and the benefits of it. See more information on the Points of Interest Section.
I included a demo project that you could download and use to test the implementation. Note that the demo project saves the ViewState to the session, which I think is the best place to store it.
Using the code
As I said, the implementation is very easy. So, let's start with my Class
code, and after discussing it, let's see the configuration of the Web.Config file.
Imports System.Configuration.ConfigurationSettings
Imports System.Diagnostics
Public Class VSPage
Inherits System.Web.UI.Page
Now, let's see the method responsible for saving the ViewState data, called SavePageStateToPersistenceMedium
. All that this method does is see if the ServerSideViewState
on our Web.Config file is enabled (True
). If no, the system will perform a normal ViewState save, storing it on the ASPX document. If yes, the system will check which is the ServerSideViewState
method that you want to use: CACHE
or SESSION
. If you choose CACHE
, the data will be stored on the cache and will expire after some time. If you choose SESSION
, the data will be saved on the user session and will be discarded when the user session expires. When this option is used, the system automatically creates a DataTable
that will be saved on the session and will hold all the ViewState data. The maximum number of ViewState data that could be held on the DataTable
is defined by the Web.Config parameter ViewStateTableSize
. The default value, 150, represents that the ViewState data of the last 150 postbacks will be available to the system. So, if the user clicks on the navigator's Back button 150 times (what is improbable to occur), the ViewState for that page will already be present to be used. This is a very good number...
Protected Overrides Sub _
SavePageStateToPersistenceMedium(ByVal viewState As Object)
Dim VSKey As String
Debug.WriteLine(MyBase.Session.SessionID)
VSKey = "VIEWSTATE_" & MyBase.Session.SessionID & "_" & _
Request.RawUrl & "_" & Date.Now.Ticks.ToString
If UCase(AppSettings("ServerSideViewState")) = "TRUE" Then
If UCase(AppSettings("ViewStateStore")) = "CACHE" Then
Cache.Add(VSKey, viewState, Nothing, _
Date.Now.AddMinutes(Session.Timeout), _
Cache.NoSlidingExpiration, _
Web.Caching.CacheItemPriority.Default, Nothing)
Else
Dim VsDataTable As DataTable
Dim DbRow As DataRow
If IsNothing(Session("__VSDataTable")) Then
Dim PkColumn(1), DbColumn As DataColumn
VsDataTable = New DataTable("VState")
DbColumn = New DataColumn("VSKey", GetType(String))
VsDataTable.Columns.Add(DbColumn)
PkColumn(0) = DbColumn
VsDataTable.PrimaryKey = PkColumn
DbColumn = New DataColumn("VSData", GetType(Object))
VsDataTable.Columns.Add(DbColumn)
DbColumn = New DataColumn("DateTime", GetType(Date))
VsDataTable.Columns.Add(DbColumn)
Else
VsDataTable = Session("__VSDataTable")
End If
DbRow = VsDataTable.Rows.Find(VSKey)
If Not IsNothing(DbRow) Then
DbRow("VsData") = viewState
Else
DbRow = VsDataTable.NewRow
DbRow("VSKey") = VSKey
DbRow("VsData") = viewState
DbRow("DateTime") = Date.Now
VsDataTable.Rows.Add(DbRow)
End If
If Convert.ToInt16(AppSettings("ViewStateTableSize"))_
< VsDataTable.Rows.Count Then
Debug.WriteLine("Deleting ViewState Created On " _
& DbRow(2) & ",ID " & DbRow(0))
VsDataTable.Rows(0).Delete()
End If
Session("__VSDataTable") = VsDataTable
End If
RegisterHiddenField("__VIEWSTATE_KEY", VSKey)
Else
MyBase.SavePageStateToPersistenceMedium(viewState)
End If
End Sub
And now, let's see the method responsible to load the ViewStateData
, LoadPageStateFromPersistenceMedium
:
Protected Overrides Function LoadPageStateFromPersistenceMedium() As Object
If UCase(AppSettings("ServerSideViewState")) = "TRUE" Then
Dim VSKey As String
VSKey = Request.Form("__VIEWSTATE_KEY")
If Not VSKey.StartsWith("VIEWSTATE_") Then
Throw New Exception("Invalid VIEWSTATE Key: " & VSKey)
End If
If UCase(AppSettings("ViewStateStore")) = "CACHE" Then
Return Cache(VSKey)
Else
Dim VsDataTable As DataTable
Dim DbRow As DataRow
VsDataTable = Session("__VSDataTable")
DbRow = VsDataTable.Rows.Find(VSKey)
If IsNothing(DbRow) Then
Throw New Exception("VIEWStateKey not Found. " & _
"Consider increasing the ViewStateTableSize" & _
" parameter on Web.Config file.")
End If
Return DbRow("VsData")
End If
Else
Return MyBase.LoadPageStateFromPersistenceMedium()
End If
End Function
End Class
And now, this is what we need to insert on our config file Web.Config:
<!---->
<appSettings>
<!---->
<add key="ServerSideViewState" value="True"/>
<!---->
<add key="ViewStateStore" value="Session" />
<!---->
<add key="ViewStateTableSize" value="150" />
</appSettings>
Points of Interest
A lot of research was done to implement the login presented on this article. One of the most interesting sources is this. If you would like to know more about these techniques and see the result of some stress tests, follow the link.
Conclusion
ViewState really simplifies the development of ASP.NET applications, but due to increase in the page load time, some developers don't like to use it. With this technique, you could have all the advantages of using ViewState on your projects without worry about the size of the generated page source code.