Storing Viewstate in other places by overriding the Page object
When building the messageboards of the 700level, I was coming across a situation where my ViewState was winding up being on the order of 50K-100K which was unacceptable for dialup users. In addition, they were the most heavily trafficked pages with users sitting on them clicking "refresh" to see the latest posts so I was afraid of what would happen to our bandwidth usage during playoff times (the site is a Philadelphia Eagles fansite). At the time, I needed the viewstate to handle events such as the PageChanged event of the DataGrid. I've since rewritten one of the DataGrids as a Repeater and implemented all my own paging code, so this code will soon come out of my site, but I wanted to document it here before that happened in case anyone else might find this useful ... perhaps on an intranet-type of project where the userbase can be calculated in advance.
I'll just paste the code and talk about it more at the end:
public
class ViewStateService : System.Web.UI.Page
{
public ViewStateService()
{
}
protected override object LoadPageStateFromPersistenceMedium()
{
LosFormatter oLosFormatter = new LosFormatter();
if (Session["ViewState"] == null)
{
Response.Redirect( "/fansview/default.aspx", true );
}
return oLosFormatter.Deserialize(Session["ViewState"].ToString());
}
protected override void SavePageStateToPersistenceMedium(object viewState)
{
LosFormatter oLosFormatter = new LosFormatter();
StringWriter oStringWriter = new StringWriter();
oLosFormatter.Serialize(oStringWriter, viewState);
Session["ViewState"] = oStringWriter.ToString();
}
protected override void Render(HtmlTextWriter writer)
{
if (Request.Path != null)
{
writer = new MyWriter(writer);
}
base.Render (writer);
}
}
Okay, looking over this, the important parts are LoadPageStateFromPersistanceMedium which now checks a Session variable instead of the hidden field on the page and SavePageStateFromPersistanceMedium which does the same thing in reverse. Notice how I check to see if the Session variable exists and, if not, I bounce the user back to the default page for that section. I couldn't think of anything better to do with a user with an expired session and I didn't want their page to blow up with the viewstate error, so I sent them back to the list of forums. The LosFormatter objects pretty much just serialize and deserialize all of the state information into the garbage that you see when you look at the viewstate.
The overriding of the Render method is being done to override the default HtmlTextWriter behavior like I describe in this post about removing the action attribute from the form tag. You can remove that safely and still have your ViewState functionality working. I just included it in this post because I said I would in my last one.
The implications of this were that I would wind up using about 80K (on average) of Session space for each user. I usually have around 30 users on the site, maybe 50 or 60 during the playoffs, and it handled it well. A quick calculation shows that 50 users would use about 4 megs of RAM ... very doable today and in my mind and adequate tradeoff for not having to force that 80K of data down a 56K pipe, especially since my graphic designer uses a lot of graphics in our sites that our users have to download, so I didn't want to make users download anything more than they needed from me. Most of the site does not use this new Page object and the pages have viewstate as normal, but the pages that form the engine that drives the messageboards do use this page (and they are by far the most heavily used pages ... I think the page that displays threads gets about 40 times the number of hits as the next most heavily used section of the site).
So I hope this helps someone out there, or at least gets them thinking about some of the possibilities of things you can do with ASP.NET. If you have time, I suggest you dig through the Page class's protected methods and see what kinds of things it's doing under the hood. You'll get a much better understanding of how pages are built by the runtime. By the way, I'm not saying that this is the BEST way to do this or that storing ViewState in Sessions is the BEST approach to handling this.