Persist the ViewState and Increase ASP.NET Performance
At the Richmond Code Camp I went over some of my tips and tricks to get ASP.NET to perform as fast as possible. One of the biggest things you have to do is reduce the size of your ViewState and also move it to the bottom of the page. It was brought to my attention that the View State can be stripped entirely from the page as it is being sent to the client (well sort of) and stored or persisted as a local file. I found Dino Esposito's Cutting Edge article on working with the ViewState where he demonstrates this principle.
I tried this with some code to see what happened and I was pleasently surprised that this was pretty quick and did not produce any ViewState related exceptions for me. Let's take a look, first at the original ViewState for a page:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJMTE2MTg3NTYyDs2QWAmYPZBYCAgEPZBYCAg4PZBYCAgEP
ZBYEZg9kFgQCAw8QZGQWAQIdBZAIfDxQrAAJkZGQCAQ9kFgQCAQ8WAh4HVmlza
WJsZWgWAgIBD2QWAgIBD2QWAgIFsDw8WAh4ISW1hZ2VVcmwFKmh0dHA6Ly9sb2
NhbGhvc3QvbWFjL2ltYWdlcy9jb250YWdN0XzMyLmdpZmRkAgUPZBYEAgEPFgIfAGcW
BAICD2QWAgIBDxYCHgRUZXh0BQ9MdaW5kYSBEZW1lcml0dGVkAgQPZBYEAgMPF
gIfAGcWAgICD2QWAgIBDxYCHwIFCfUFsYW4gSHVudGQCBQ8PFgIfAGhkZAICD2QW
AmYPZBYCAgEPFgIfAGhkGAEFJGNf0bDAwJENvbnRlbnRQbGFjZUhvbGRlcjEkTXVsdG
lWaWV3MQ8PZAIBZBLDmk8f72Yt1WKBfAWhD8KS0hJhM" />
Now this is the ViewState Input tag using the PageState Persistance:
<input type="hidden" name="__VIEWSTATE" id="
__VIEWSTATE" value="" />
As you can see this is quite an improvement. What happens is a file is created on the web server that contains the Page's ViewState. The file contains the following text and is named after the session state.
/wEPGAEFJGN0bDAwJENvbnRlbnRQbGFjZUhvbGRlcjfEkTXVsdGlWaWV3MQ8PZAIBZA8FCTExNjE4Nz
U2Mg9kFgJmD2QWAgIBD2QWAgIOD2QWAgIBD2QWBGfYPZBYEAgMPEGRkFgECAWQCHw8UKwAC
ZGRkAgEPZBYEAgEPFgIeB1Zpc2libGVoFgICAQ9kFgICAQf9kFgICBQ8PFgIeCEltYWdlVXJsBSpodHRw
Oi8vbG9jYWxob3N0L21hYy9pbWFnZXMvY29udGFjdF8zMi5naWZkZAIFD2QWBAIBDxYCHwBnFgQCAg
9kFgICAQ8WAh4EVGV4dAUPTGluZGEgRGVtZfXJpdHRlZAIED2QWBAIDDxYCHwBnFgICAg9kFgICAQ8
WAh8CBQlBbGFuIEh1bnRkAgUPDxYCHwBofZGQCAg9kFgJmD2QWAgIBDxYCHwBoZA==
Now this leaves a problem because the ViewState files are bound to build up. So you will have to build a purging mechanism into the web application to keep the directory as clean as possible. For security purposes you also may not be allowed to store media either created on the fly or uploaded by users from the web. I decided a good way to counter this problem would be to store the ViewState in cache with a dependancy. This of course causes considerations for memory usage, so you will have to decide what is the most important in your situation.
I like to use the Enterprise Library to handle my Caching needs, so you will need to investigate this on your on, or wait will I get a chance to Blog about using the Caching block. But until then, trust me this works. The methodology relies on two special methods being created. I created these methods to abstract the methodology out of the two Page class methods that need to be overriden. I also created a utility method to return the sessionid as a name for the Cache variable, again just to abstract this out.
Protected
Function
LoadPageStateFromCache()As
Object
Dim
m_viewStateAs
String
Dim
m_formatterAs
LosFormatter
Dim
viewStateBagAs
Object
Dim
pcAs
CacheManager = CacheFactory.GetCacheManager()
Dim
sRequestContentAs
String
=DirectCast
(pc.GetData(GetCacheName),String
)
If
String
.IsNullOrEmpty(sRequestContent) =False
Then
Dim
srAs
New
StringReader(sRequestContent)m_viewState = sr.ReadToEnd()
sr.Close()
m_formatter =
New
LosFormatter()
viewStateBag = m_formatter.Deserialize(m_viewState)
End
If
Return
String
.Empty
End
Function
Protected
Sub
SavePageStateToCache(ByVal
viewStateBagAs
Object
)
Dim
m_formatterAs
LosFormatter =New
LosFormatter()
Dim
stwAs
StringWriter =New
StringWriterm_formatter.Serialize(stw, viewStateBag)
Dim
primitivesCacheAs
CacheManager = CacheFactory.GetCacheManager()primitivesCache.Add(GetCacheName, stw.ToString(), CacheItemPriority.Normal,
New
ExCacheRefreshAction, _
New
AbsoluteTime(DateAdd(DateInterval.Minute, Session.Timeout + 30, Now())))
End
Sub
Protected
Overloads
Overrides
Function
LoadPageStateFromPersistenceMedium()As
Object
Return
LoadPageStateFromCache()
'Return LoadPageStateFromFile()
'Return MyBase.LoadPageStateFromPersistenceMedium()
End
Function
Protected
Overloads
Overrides
Sub
SavePageStateToPersistenceMedium(ByVal
viewStateBagAs
Object
)
SavePageStateToCache(viewStateBag)
'SavePageStateToFile(viewStateBag)
'MyBase.SavePageStateToPersistenceMedium(viewStateBag)
End
Sub
Private
Function
GetCacheName()As
String
Return
Session.SessionID.ToString
End
Function
One trick I do, just to be a little safe is to tell the Cache dependancy to keep the value in memory for 30 minutes past the session timeout. What this will lead us to is creating a handler to let the user know their session has timed out. I know I am real bad about always having my computer on and on about 20 pages all the time. That means that many of these stateless pages will eventually cause a post back to the server when I get back to it. My online banking application does this if I leave it running without a postback in say 10 minutes, so it is a common scenario to handle. That will lead to another Blog entry. I hope to have that ready by the NJ Code Camp this weekend.