Move the ViewState to Session and eliminate page bloat
In my continuing theme on reducing and eliminating the ViewState from ASP.NET pages I want to begin covering some great new features to help ViewState in ASP.NET 2.0. Last month I talked about persisting the ViewState to memory using the LosFormatter. That technique works fine, although there can be some gotchas to any technique to manage ViewState besides the default. One problem with that technique is it is optimized for ASP.NET 1.1 and the LosFormatter, which while still functional in 2.0 is not the best way anymore. ASP.NET 2.0 relies heavily on the provider to manage almost every aspect of the execution of the framework. ViewState is no different and as such allows us to leverage different ViewState or PageState providers. All ViewState providers inherit from the PageStateProvider class, an abstract or must inherit class in the framework.
The default PageState provider is the HiddenFieldPageStatePersister, which is what we see when we do a view source on an ASP.NET page. It is also the control that can get pretty large if we drop too many controls and data in the ViewState of a page. To get back to our idea of storing the ViewState in memory the nice guys on the ASP.NET team have given us an alternative out of the box, SessionStatePagePersister.
The SessionStatePagePersister is a new approach to ViewState in ASP.NET 2.0 because it stores the value in session memory, but stores a collection of ViewState values. The default number of items is 9, an arbitrary amount. You can set this value in the Web.Config file by adding the following section to the Web.Config file:
<sessionPageState historySize="20" />
The reason you would need to store each instance of ViewState is the case of multiple browsers being used in a session, think of pop-up windows, etc. The other case is when a user clicks that back button in the browser. What happens in each of these cases is the page will not reference the proper ViewState values because there is only one ViewState value persisted to either memory or a file. The way around this is to build a multi-facited naming convention for each page, that also means each time the page has been loaded. So basically this can become real tricky to properly implement. Now the whole hidden field thing has begun to make more sense to me :>.
To actually apply the SessionStatePagePersister to a site you have to override the setting in the PageAdapter for the site (this is starting to get a little dragged out now, but hang in there). First we need to create a custom PageAdapter. I am not going to cover what these are right now, but a quick summary of a page adapter is it is the class that defines how a page is rendered to different browsers. The code for our custom PageAdapter class:
It is simply one method override, GetStatePersister. Now we need to register our PageAdapter class, this is done by creating a browser file. Browser files are in the App_Browsers directory and is a set of XML that further define how the site will handle different browsers. In our case we need a real simple .browser file.
<adapter controlType="System.Web.UI.Page" adapterType="MyPageStateAdapter" />
Now all that is left is to save all of our site updates and create a test page. I created a crazy little page with some random controls and some response.write commands when the button is clicked to display some relevant information to prove we have a new ViewState persister active.
There does not seem to be a whole lot out there at this point on this solution, but I did seem to have some difficulties the first time I implemented it with a GridView control. So I will continue to do some research on this topic.
Stay tuned for more on this topic. I know there is a good solution to managing ViewState so it does not have to get sent to every browser, while maintaining a high performance site. Next I plan to create a custom PageStatePersister to handle more aspects of storing ViewState on the server that should extend the SessionPageStateProvider we are using here.