ViewState is a feature provided by ASP.Net that stores information for server controls used on a web page. The primary advantage of this feature from a developer perspective is that it keeps the values of controls on a form. If the user forgot a single required field on a form, ASP.Net can put back the other values they had entered rather than forcing the developer to repopulate them. The information is encoded into a string and placed in a hidden form field called “__VIEWSTATE” that is sent to the client with the response (you can see this by using View Source in your browser when you view a .aspx page). ASP.Net by default also calculates a “hash” of the information and adds it to the ViewState, then on a postback it can validate that the data has not been corrupted. That is a basic description of what viewstate is and how it is implemented, this article will focus on where the viewstate information is stored.
After converting our web site from ASP to ASP.Net, we began to have problems with viewstate exceptions. We would receive "The View State is invalid for this page and might be corrupted" about 50 times a day. We searched for known causes for this problem and none of them applied to our situation. After several network traces and calls to Microsoft Support, we determined that when network retransmissions occurred the client would sometimes have a corrupt version of the HTML page (we were never able to determine why TCP did not detect the corruption). If the corruption happened to occur inside the viewstate hidden field value, it would cause the exceptions (the recomputed hash on the server did not match the original hash). We also determined that several browsers (such as older WebTV clients) could not handle large hidden form fields. Our solution was to save the viewstate for the pages on the web server using the Session object instead of the HTML result sent to the client.
Benefits of Server Side ViewState
- Avoid corruption problems
- Avoid issues with browsers that limit the maximum size of hidden fields
- Reduced client page size - since the viewstate information is stored on the server it is not sent in the response to the client which saves download time and bandwidth
Disadvantages of Server Side ViewState
- Uses server side memory for the in-process session object
- Not as obvious to developers how much space is used by viewstate
The idea is to override the default handling of viewstate provided by ASP.Net and implement a custom implementation to store the viewstate information on the server. Originally I was not enthusiastic about the idea of rewriting some of the basic functionality provided by ASP.Net, but it was very easy to do and eliminated our problems.
Since the viewstate is specific to each user, we decided to store the information using the Session object. Our servers use in-process session state (stored in web server memory), so we wanted to limit the amount of viewstate we had to store for each user. We cannot simply store the last viewstate for user, however, because a user can visit a page in Internet Explorer, hit the back button to a previous page and push a button (which would require the viewstate for that page). This requires us to support multiple versions of viewstate for each user. The number of page viewstate values to store is a tradeoff between memory and support for pressing the back button a number of times. The example solution stores the last 5 viewstate values for each user. The viewstate values are stored in a circular array using the modulus operator (% in C#). Every time a user requests a new page on the site, the code increments a request number and uses that number as a key to the proper viewstate in the array on the server. The code uses the modulus of the request number and the size of the array to determine the index into the circular array. This starts storing viewstate for the first page (request number 1) in index 1 of the array (1 % 5 = 1), request 2 would use index 2, etc. This continues until the request number hits 5, which wraps back around and uses index position 0. This is a simple way to use a fixed size array efficiently. The code stores the request number in the “__VIEWSTATE” field that was previously used to store the actual viewstate information. The code consults the web.config file to determine if it should use server side or client side viewstate (changes to the web.config will drop the in-process Session object for the user which will drop all current viewstate, make changes to this during off peak times).
Instructions for Using
The first step in implementing the solution is to download the two C# files
that implement the server side functionality and add them to your ASP.Net project.
This file includes the class BasePage
which is used as a parent class for all pages on the site and can be used without modification. The use of a custom base class has many advantages for an ASP.Net site, in this case it is used to override the default viewstate handling provided by System.Web.UI.Page. The two methods that this class overrides are LoadPageStateFromPersistenceMedium() which is called to reload the viewstate on a postback, and SavePageStateToPersistenceMedium() which is called to save the viewstate before sending the results to the client. The override of these methods simply check if server side viewstate is enabled, then call methods to do the work if it is (otherwise they just invoke the original Page class methods). The LoadViewState() method checks for the __VIEWSTATE field and attempts to convert it to a long (which would fail if the viewstate was actually the original encoded string). If it successfully retrieves the value, it uses another class viewStateSvrMgr
to retrieve the viewstate from the session. The SaveViewState() method uses the viewStateSvrMgr
class to save the viewstate to the session, and writes the hidden form field __VIEWSTATE to the client page with the request number key. If you are using the .Net 1.1 framework, you should change the name used for the hidden field value, which is stored in the constant VIEW_STATE_FIELD_NAME in this file (anything other than __VIEWSTATE).
All web pages for the site must derive from the BasePage
class. The default file webForm1.aspx.cs in an ASP.Net project has the original class declaration “public class WebForm1 : System.Web.UI.Page”, this is changed instead to “public class WebForm1 : ServerSideViewstate.BasePage”.
This provides the majority of the code required for server side viewstate. A constant at the top of the file VIEW_STATE_NUM_PAGES controls the number of viewstate values kept for each user. This can be adjusted as necessary to conserve memory, but if the user hit the back button more times than this array size and did a postback, the viewstate data would not be present which will affect page behavior for controls and events. This file also uses a technique that stores an object in session rather than storing multiple independent session variables. The GetViewStateSvrMgr() method checks if an object of this class has already been created and stored in session or if it needs to do so. Then the class can simply store local variables that are all stored in that single session object, which helps avoid pollution of the Session with lots of independent keys with no relation between them. Pages that need to use the services of the class call GetViewStateSvrMgr() and get an instance of the class they can use to get/save viewstate information. The property ServerSideEnabled abstracts the check for whether the server side viewstate is enabled or not (via the web.config setting). The methods SaveViewState() and GetViewState() manage the circular array which is a member of the class, and hence is stored in the object that is placed in the session object for this user.
The last step is to add the following to the web.config file (place just above the </configuration> entry:<appSettings>
<add key="ServerSideViewState" value="true" />
Once the files have been added to the project, the pages have been changed to derive from the BasePage
class, and the changes to web.config have been made the site will start to use the session to store viewstate. This can be validated by visiting one of the .aspx pages on the site and using the View Source option of the browser, search for the __VIEWSTATE and notice that it is no longer a large encoded string, but a request number that is the key to the location of the viewstate for the page on the server.
This solution still supports the EnableViewState property for pages and controls and the viewstate size can be seen with tracing, this technique simply changes where the viewstate will be stored. The original driver of using Server Side ViewState was some corruption problems and lack of browser support for large hidden form fields. The use of the described technique solves both of those problems, while also reducing the page size sent to the client.
Send comments or questions to firstname.lastname@example.org.