Reduce
View State Size
Introduction
Many Sitefinity sites are based on Web Forms and Web Forms use ViewState. It is common knowledge that uncontrolled ViewState can create a very large amount of data in our web pages and this data is sent back and forth with every request. There are many ways to reduce and turn off ViewState when it is not required but when it is there is one more method to help keep down the page size.
Before we start I would like to point out that this isn't going to be an article discussing how to maintain state for a user across Http requests over the internet. I am assuming you have already looked at your options and decided on what suits your purpose. This article is just about how to maintain ViewState, because you are using it, in a more performant manner in Sitefinity.
I am also assuming you understand what ViewState and Session State are and how they work.
ViewState
Most of us know about the hidden ViewState data stored in a hidden html input field in a Web Forms page. We are often encouraged to minimise the use of ViewState as much as possible as the size of this field can be massive and this data is sent back and forth with every request to a Web Forms page. By default Sitefinity has ViewState disabled for pages as most pages don't need it.
But sometimes you do.
If you take advantage of the Telerik Web Controls that come with Sitefinity you will know how super easy these are to implement and achieve some functionality you are after. But some of the functionality requires ViewState to be enabled.
An Oldie but a Goodie
Some of you may be too young to recall this but you don't have to save your ViewState in a hidden input field and post it back and forth with every request. You can store it in your sessions state.
Simply, you override your pages and tell them to store your ViewState data in your Session store. Your ViewState input field is still there but all it holds is the 'key' to retrieve it from the session store. This can vastly reduce the number of bytes sent across the wire and speed up the performance of each of your pages.
There are two options you can use in Sitefinity. (Or any ASP.NET Web Application for that matter.) A global option or a per page option. I will detail the two and you can decide which is best.
Quick Interjection
First, yes your CPU will be used more but I would say this is negligible.
Second, you do have to consider the increase in Session storage required.
By default Session State is stored InProc with Web Forms. This means local memory so if you have limited memory availability then this may not be a good option.
The next most common option is storing Session State in a SQL database. Plenty of space there and this may be required if you are running multiple nodes. This isn't the only option if you have multiple nodes and I would always discourage storing session state there.
When you are storing session state data in a SQL database this requires every request to do two calls to your database. The first to retrieve the session data at the beginning of the request and the second to update the session data at the end.
Third option, (and my preferred), is an external distributed cache system like, (some of you just know what I am going to trumpet about), the Azure Redis Cache.
So have a think about what you are\will be using and how you will be impacted.
The Global Option
One option is to tell your whole application to store ViewState in your session. We start by creating a class file that inherits from the PageAdapter class and tell it to return a SessionPageStatePersister type by default.
Next we add a special folder called App_Browser and add a .browser file to it which says use our class.
Yip, that's it.
Almost
There are some pages in Sitefinity that will not work with a Session State Persister. The Sitefinity logon page is one. (The authentication is not Web Forms based and that messes with it.) In my case I am only looking to improve the front end performance and so I put in a simple test. If its a Sitefinity page, (starts with /Sitefinity), then use the standard HiddenFieldPageStatePersister else return a Session one.
public class ViewStateAdapter : PageAdapter { public override PageStatePersister GetStatePersister() { if (this.Page.Request.Url.AbsolutePath.StartsWith("/Sitefinity")) { return new HiddenFieldPageStatePersister(this.Page); } return new SessionPageStatePersister(this.Page); } }
<browserrefID="Default"> <controlAdapters> <adaptercontrolType="System.Web.UI.Page" adapterType="Pdr.Sitefinity.Stuff.ViewStateAdapter" /> </controlAdapters> </browser>
Page Required Option
Option two is we create a class that inherits from the Sytem.Web.UI.Page class and override the PageStatePersister method. On the pages that we want to target we can then assign it in the pages properties.
Pretty simple.
public class OurPage : System.Web.UI.Page { protected override PageStatePersister PageStatePersister { get { return new SessionPageStatePersister(Page); } } }
The Results
Below are a couple of screen shots of before and after ViewState sizes. Both methods return the same result.
Generally using ViewState and Session State is frowned on by the smarties and for good reasons. But sometimes it does what we need with out any hassles and is a good option for our projects. If that is the case we can alleviate one of the downsides of page size by storing our ViewState in our Session data rather than transmitting it across the wire every time.
Thanks for reading and feel free to comment - Darrin Robertson
If I was really helpful and you would buy me a coffee if you could, yay! You can.
Make a Comment