Introduction
From an SEO
standpoint, page size matters, as well as the location of text on your
page. When creating a site with VS .Net,
we end up with a ViewState variable, which in some cases, can take up a whole
lot of space near the top of the page.
This could, in fact, dilute the contents' importance on a given web
page. In terms of page size, some search
engines only cache the first 120kb of data.
This project
was an attempt at finding another location to store the ViewState. In this case, I made four locations available
for storage:
- Session
- Application
- AppGlobals
- Cache
This can be easily implemented
in any C# project by creating a base class, referencing it in all classes, and
selecting the storage location in an appSettings key in the Web.config. What is missing, however, is a SQL Server
storage location. I'd be quite willing
to incorporate at any such implementations!
Before fully diving
into the code, I'd like to first discuss why it's important, and how the PageStatePersister,
really didn't help.
Background
Enter the importance
of reducing the ViewState parameter size.
My initial reason for writing this code/putting it together, are a bit
different that Oleg Sobol's in this (ViewStateProvider_Net2_0.asp)
article, but I think are just as applicable, but our core reason is the same � ViewState
size � it does matter.
Unfortunately
it's been a while since I wrote this internal article, and haven't fully
referenced all the sources. So if I've
used some of your code, I apologize in advance.
ASP.Net ViewState Management
I tested using
no ViewState management, ASP.Net 2.0's PageStatePersister, and the server
caching method.
My overall
results show that the server caching method is superior to the others. In
some cases, the payload of the PageStatePersister is about equal to that of the
server cache method, but on pages that contain a higher number of server
controls, the PageStatePersister falls dramatically short of the server cache
method (see Login.aspx below).
Server caching
offers four ViewState stores - session, application, appglobals, cache.
For testing purposes I only used server caching, as the data below reflects it
is the superior method.
It is my
conclusion, based on this and the benchmark tests located at http://www.eggheadcafe.com/articles/20040613.asp,
that we use the server cache method.
ViewState test
without the server-side scheme is in the first column; the four server side
schemes follow:
|
VIEWSTATE
WEB STRESS COMPARISON
|
|
TEST
NAME
|
VIEWSTATE
|
SESSION
|
APPLICATION
|
CACHE
|
GLOBALS
|
NUMBER
OF HITS
|
3322
|
18483
|
18476
|
20170
|
16723
|
REQUESTS
/SEC
|
27.64
|
153.8
|
153.74
|
167.84
|
140.32
|
TOTAL
BYTES REC'D (KB)
|
68016.63
|
22923.25
|
22977.13
|
25074.51
|
20798
|
PERCENT
DIFFERENCE
|
N/A
|
456.44%
|
456.22%
|
507.24%
|
407.67%
|
Regarding the
server stores method, I've used the same design pattern myself � first routing
data through a base class � very slick.
I've created an
assembly containing the ViewState handler, which I can import into any future
projects that use these classes. Once again � write once, use
anywhere. I also converted some of the code for better usability with
2.0.
Test results
below:
Default01.aspx
No Viewstate
Management: ~ 329 characters | Payload: 1k
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwULLTExMjY5MTM5MTYPZBYCAgMPZBYCAgsPZBYCAgEPFgIeC18h
SXRlbUNvdW50AgUWCmYPZBYCZg8VAQdBZHZhbnRhZAIBD2QWAmYPFQEQQW
1lcmljYW4gRXhwcmVzc2QCAg9kFgJmDxUBCUJBTktGSVJTVGQCAw9kFgJmDxUB
BUNoYXNlZAIED2QWAmYPFQEXQ29sdW1idXMgQmFuayBhbmQgVHJ1c3RkZHE/U
FRpXKP0LBuXCsdD3lver4Y3"
/>
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwULLTExMjY5MTM5MTYPZBYCAgMPZBYCAgsPZBYCAgEPFgIeC18h
SXRlbUNvdW50AgUWCmYPZBYCZg8VAQdBZHZhbnRhZAIBD2QWAmYPFQEQQW
1lcmljYW4gRXhwcmVzc2QCAg9kFgJmDxUBCUJBTktGSVJTVGQCAw9kFgJmDxUB
BUNoYXNlZAIED2QWAmYPFQEXQ29sdW1idXMgQmFuayBhbmQgVHJ1c3RkZHE/U
FRpXKP0LBuXCsdD3lver4Y3"
/>
PageStatePersister:
~ 129 characters | Payload: < 1k
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPaA8FDzhjODNlOWE3YzI3MGY1OGRg4WBM0VRZaUOeHh7vqbaRwGRdYA=="
/>
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPaA8FDzhjODNlOWMyNTU5MGZlZWSRqIAAKVQl2MXv4b2MnOhsDZhOJA=="
/>
Server Cache
Viewstate Storage: ~ 140 characters | Payload: < 1k
<input
type="hidden" name="__VIEWSTATE1"
id="__VIEWSTATE1" value="4" />
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="" />
<input
type="hidden" name="__VIEWSTATE1"
id="__VIEWSTATE1" value="9" />
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="" />
No Viewstate
Management: ~ 349 characters | Payload: ~ 1k
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwUJMzE0MTExNjQyD2QWAgIDD2QWAgIED2QWAgIBDxYCHgtfIUl0ZW
1Db3VudAIFFgpmD2QWAmYPFQEHQWR2YW50YWQCAQ9kFgJmDxUBEEFtZXJpY
2FuIEV4cHJlc3NkAgIPZBYCZg8VAQlCQU5LRklSU1RkAgMPZBYCZg8VAQVDaGFzZWQ
CBA9kFgJmDxUBF0NvbHVtYnVzIEJhbmsgYW5kIFRydXN0ZBgBBQlHcmlkVmlldzEPZ2R
mesVnAmycBPQuwY/+R2iSTw8AgA=="
/>
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwUJMzE0MTExNjQyD2QWAgIDD2QWAgIED2QWAgIBDxYCHgtfIUl0ZW1
Db3VudAIFFgpmD2QWAmYPFQEHQWR2YW50YWQCAQ9kFgJmDxUBEEFtZXJpY2F
uIEV4cHJlc3NkAgIPZBYCZg8VAQlCQU5LRklSU1RkAgMPZBYCZg8VAQVDaGFzZWQCB
A9kFgJmDxUBF0NvbHVtYnVzIEJhbmsgYW5kIFRydXN0ZBgBBQlHcmlkVmlldzEPZ2Rme
sVnAmycBPQuwY/+R2iSTw8AgA=="
/>
PageStatePersister:
~ 149 characters | Payload: < 1k
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPaA8FDzhjODNlOWQ1YTljYjg0NBgBBQlHcmlkVmlldzEPZ2S3+UlLsRyU8KTbI
sZjQZTqo3Eh2Q=="
/>
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPaA8FDzhjODNlOWQ2MzI3MDRhNhgBBQlHcmlkVmlldzEPZ2R0CWKRyWY
H15G9LmpwwFnocxQkxQ=="
/>
Server Cache
Viewstate Storage: ~ 141 characters | Payload: < 1k
<input
type="hidden" name="__VIEWSTATE1"
id="__VIEWSTATE1" value="11" />
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="" />
Login.aspx
No Viewstate
Management: ~ 1345 characters | Payload: ~ 2k
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwUKLTM3MDg0NzE3NWQYAwUeX19Db250cm9sc1JlcXVpcmVQb3N0QmF
ja0tleV9fFg0FEUxvZ2luMSRSZW1lbWJlck1lBRdMb2dpbjEkTG9naW5JbWFnZUJ1dHRvbg
U8Q3JlYXRlVXNlcldpemFyZDEkX19DdXN0b21OYXYwJFN0ZXBQcmV2aW91c0J1dHRvbk
ltYWdlQnV0dG9uBThDcmVhdGVVc2VyV2l6YXJkMSRfX0N1c3RvbU5hdjAkU3RlcE5leHRCd
XR0b25JbWFnZUJ1dHRvbgU2Q3JlYXRlVXNlcldpemFyZDEkX19DdXN0b21OYXYwJENhbm
NlbEJ1dHRvbkltYWdlQnV0dG9uBUlDcmVhdGVVc2VyV2l6YXJkMSRTdGFydE5hdmlnYXRp
b25UZW1wbGF0ZUNvbnRhaW5lcklEJFN0YXJ0TmV4dEltYWdlQnV0dG9uBUZDcmVhdGVV
c2VyV2l6YXJkMSRTdGFydE5hdmlnYXRpb25UZW1wbGF0ZUNvbnRhaW5lcklEJENhbmNlb
EltYWdlQnV0dG9uBU9DcmVhdGVVc2VyV2l6YXJkMSRGaW5pc2hOYXZpZ2F0aW9uVGVtc
GxhdGVDb250YWluZXJJRCRGaW5pc2hQcmV2aW91c0ltYWdlQnV0dG9uBUdDcmVhdGVV
c2VyV2l6YXJkMSRGaW5pc2hOYXZpZ2F0aW9uVGVtcGxhdGVDb250YWluZXJJRCRGaW5p
c2hJbWFnZUJ1dHRvbgVHQ3JlYXRlVXNlcldpemFyZDEkRmluaXNoTmF2aWdhdGlvblRlbXBs
YXRlQ29udGFpbmVySUQkQ2FuY2VsSW1hZ2VCdXR0b24FS0NyZWF0ZVVzZXJXaXphcmQx
JFN0ZXBOYXZpZ2F0aW9uVGVtcGxhdGVDb250YWluZXJJRCRTdGVwUHJldmlvdXNJbWFnZ
UJ1dHRvbgVHQ3JlYXRlVXNlcldpemFyZDEkU3RlcE5hdmlnYXRpb25UZW1wbGF0ZUNvbnRh
aW5lcklEJFN0ZXBOZXh0SW1hZ2VCdXR0b24FRUNyZWF0ZVVzZXJXaXphcmQxJFN0ZXBOY
XZpZ2F0aW9uVGVtcGxhdGVDb250YWluZXJJRCRDYW5jZWxJbWFnZUJ1dHRvbgURQ3JlY
XRlVXNlcldpemFyZDEPEGQUKwABZmZkBSFDcmVhdGVVc2VyV2l6YXJkMSRXaXphcmRN
dWx0aVZpZXcPD2RmZIG8LAAmxd9A5eBGBmrtERp+z8nr"
/>
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwUKLTM3MDg0NzE3NWQYAwUeX19Db250cm9sc1JlcXVpcmVQb3N0QmF
ja0tleV9fFg0FEUxvZ2luMSRSZW1lbWJlck1lBRdMb2dpbjEkTG9naW5JbWFnZUJ1dHRvbg
U8Q3JlYXRlVXNlcldpemFyZDEkX19DdXN0b21OYXYwJFN0ZXBQcmV2aW91c0J1dHRvbkl
tYWdlQnV0dG9uBThDcmVhdGVVc2VyV2l6YXJkMSRfX0N1c3RvbU5hdjAkU3RlcE5leHRCd
XR0b25JbWFnZUJ1dHRvbgU2Q3JlYXRlVXNlcldpemFyZDEkX19DdXN0b21OYXYwJENhb
mNlbEJ1dHRvbkltYWdlQnV0dG9uBUlDcmVhdGVVc2VyV2l6YXJkMSRTdGFydE5hdmlnYX
Rpb25UZW1wbGF0ZUNvbnRhaW5lcklEJFN0YXJ0TmV4dEltYWdlQnV0dG9uBUZDcmVhd
GVVc2VyV2l6YXJkMSRTdGFydE5hdmlnYXRpb25UZW1wbGF0ZUNvbnRhaW5lcklEJENhb
mNlbEltYWdlQnV0dG9uBU9DcmVhdGVVc2VyV2l6YXJkMSRGaW5pc2hOYXZpZ2F0aW9u
VGVtcGxhdGVDb250YWluZXJJRCRGaW5pc2hQcmV2aW91c0ltYWdlQnV0dG9uBUdDc
mVhdGVVc2VyV2l6YXJkMSRGaW5pc2hOYXZpZ2F0aW9uVGVtcGxhdGVDb250YWluZXJ
JRCRGaW5pc2hJbWFnZUJ1dHRvbgVHQ3JlYXRlVXNlcldpemFyZDEkRmluaXNoTmF2aWd
hdGlvblRlbXBsYXRlQ29udGFpbmVySUQkQ2FuY2VsSW1hZ2VCdXR0b24FS0NyZWF0ZVV
zZXJXaXphcmQxJFN0ZXBOYXZpZ2F0aW9uVGVtcGxhdGVDb250YWluZXJJRCRTdGVwU
HJldmlvdXNJbWFnZUJ1dHRvbgVHQ3JlYXRlVXNlcldpemFyZDEkU3RlcE5hdmlnYXRpb25U
ZW1wbGF0ZUNvbnRhaW5lcklEJFN0ZXBOZXh0SW1hZ2VCdXR0b24FRUNyZWF0ZVVzZX
JXaXphcmQxJFN0ZXBOYXZpZ2F0aW9uVGVtcGxhdGVDb250YWluZXJJRCRDYW5jZWxJ
bWFnZUJ1dHRvbgURQ3JlYXRlVXNlcldpemFyZDEPEGQUKwABZmZkBSFDcmVhdGVVc
2VyV2l6YXJkMSRXaXphcmRNdWx0aVZpZXcPD2RmZIG8LAAmxd9A5eBGBmrtERp+z8nr"
/>
PageStatePersister:
~ 1353 characters | Payload: ~ 2k
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPaA8FDzhjODNlOWI2ODhhNzZiNBgDBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3
RCYWNrS2V5X18WDQURTG9naW4xJFJlbWVtYmVyTWUFF0xvZ2luMSRMb2dpbkltYWdl
QnV0dG9uBTxDcmVhdGVVc2VyV2l6YXJkMSRfX0N1c3RvbU5hdjAkU3RlcFByZXZpb3VzQ
nV0dG9uSW1hZ2VCdXR0b24FOENyZWF0ZVVzZXJXaXphcmQxJF9fQ3VzdG9tTmF2MCR
TdGVwTmV4dEJ1dHRvbkltYWdlQnV0dG9uBTZDcmVhdGVVc2VyV2l6YXJkMSRfX0N1c3Rvb
U5hdjAkQ2FuY2VsQnV0dG9uSW1hZ2VCdXR0b24FSUNyZWF0ZVVzZXJXaXphcmQxJFN0Y
XJ0TmF2aWdhdGlvblRlbXBsYXRlQ29udGFpbmVySUQkU3RhcnROZXh0SW1hZ2VCdXR0
b24FRkNyZWF0ZVVzZXJXaXphcmQxJFN0YXJ0TmF2aWdhdGlvblRlbXBsYXRlQ29udGFpb
mVySUQkQ2FuY2VsSW1hZ2VCdXR0b24FT0NyZWF0ZVVzZXJXaXphcmQxJEZpbmlzaE5h
dmlnYXRpb25UZW1wbGF0ZUNvbnRhaW5lcklEJEZpbmlzaFByZXZpb3VzSW1hZ2VCdXR0
b24FR0NyZWF0ZVVzZXJXaXphcmQxJEZpbmlzaE5hdmlnYXRpb25UZW1wbGF0ZUNvbnR
haW5lcklEJEZpbmlzaEltYWdlQnV0dG9uBUdDcmVhdGVVc2VyV2l6YXJkMSRGaW5pc2hOY
XZpZ2F0aW9uVGVtcGxhdGVDb250YWluZXJJRCRDYW5jZWxJbWFnZUJ1dHRvbgVLQ3JlY
XRlVXNlcldpemFyZDEkU3RlcE5hdmlnYXRpb25UZW1wbGF0ZUNvbnRhaW5lcklEJFN0ZXBQ
cmV2aW91c0ltYWdlQnV0dG9uBUdDcmVhdGVVc2VyV2l6YXJkMSRTdGVwTmF2aWdhdGlvb
lRlbXBsYXRlQ29udGFpbmVySUQkU3RlcE5leHRJbWFnZUJ1dHRvbgVFQ3JlYXRlVXNlcldpem
FyZDEkU3RlcE5hdmlnYXRpb25UZW1wbGF0ZUNvbnRhaW5lcklEJENhbmNlbEltYWdlQnV0d
G9uBRFDcmVhdGVVc2VyV2l6YXJkMQ8QZBQrAAFmZmQFIUNyZWF0ZVVzZXJXaXphcmQ
xJFdpemFyZE11bHRpVmlldw8PZGZkplxAEGixaZpDZjFXu/FEiNL4p5U="
/>
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPaA8FDzhjODNlOWJiMTBiNTk5ZRgDBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RC
YWNrS2V5X18WDQURTG9naW4xJFJlbWVtYmVyTWUFF0xvZ2luMSRMb2dpbkltYWdlQnV
0dG9uBTxDcmVhdGVVc2VyV2l6YXJkMSRfX0N1c3RvbU5hdjAkU3RlcFByZXZpb3VzQnV0d
G9uSW1hZ2VCdXR0b24FOENyZWF0ZVVzZXJXaXphcmQxJF9fQ3VzdG9tTmF2MCRTdG
VwTmV4dEJ1dHRvbkltYWdlQnV0dG9uBTZDcmVhdGVVc2VyV2l6YXJkMSRfX0N1c3RvbU
5hdjAkQ2FuY2VsQnV0dG9uSW1hZ2VCdXR0b24FSUNyZWF0ZVVzZXJXaXphcmQxJFN0Y
XJ0TmF2aWdhdGlvblRlbXBsYXRlQ29udGFpbmVySUQkU3RhcnROZXh0SW1hZ2VCdXR0
b24FRkNyZWF0ZVVzZXJXaXphcmQxJFN0YXJ0TmF2aWdhdGlvblRlbXBsYXRlQ29udGFp
bmVySUQkQ2FuY2VsSW1hZ2VCdXR0b24FT0NyZWF0ZVVzZXJXaXphcmQxJEZpbmlzaE
5hdmlnYXRpb25UZW1wbGF0ZUNvbnRhaW5lcklEJEZpbmlzaFByZXZpb3VzSW1hZ2VCdX
R0b24FR0NyZWF0ZVVzZXJXaXphcmQxJEZpbmlzaE5hdmlnYXRpb25UZW1wbGF0ZUNvb
nRhaW5lcklEJEZpbmlzaEltYWdlQnV0dG9uBUdDcmVhdGVVc2VyV2l6YXJkMSRGaW5pc2
hOYXZpZ2F0aW9uVGVtcGxhdGVDb250YWluZXJJRCRDYW5jZWxJbWFnZUJ1dHRvbgV
LQ3JlYXRlVXNlcldpemFyZDEkU3RlcE5hdmlnYXRpb25UZW1wbGF0ZUNvbnRhaW5lcklEJF
N0ZXBQcmV2aW91c0ltYWdlQnV0dG9uBUdDcmVhdGVVc2VyV2l6YXJkMSRTdGVwTmF
2aWdhdGlvblRlbXBsYXRlQ29udGFpbmVySUQkU3RlcE5leHRJbWFnZUJ1dHRvbgVFQ3Jl
YXRlVXNlcldpemFyZDEkU3RlcE5hdmlnYXRpb25UZW1wbGF0ZUNvbnRhaW5lcklEJENhb
mNlbEltYWdlQnV0dG9uBRFDcmVhdGVVc2VyV2l6YXJkMQ8QZBQrAAFmZmQFIUNyZWF
0ZVVzZXJXaXphcmQxJFdpemFyZE11bHRpVmlldw8PZGZk1a3F/q3nBJJ0+8canPDJgQV
8YLE="
/>
Server Cache
Viewstate Storage: ~ 141 characters | Payload: < 1k
<input
type="hidden" name="__VIEWSTATE1"
id="__VIEWSTATE1" value="1" />
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="" />
<input
type="hidden" name="__VIEWSTATE1"
id="__VIEWSTATE1" value="2" />
<input
type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="" />
Managing the ViewState
The
solution is to create a base class to manage the Viewstate.
Create an
Assembly called StaticAppGlobals where three files will reside:
- AppGlobal.cs
- BasePage.cs (this is your base
class that will be inherited in all classes)
- viewStateSvrMgr.cs
Setting the ViewState Storage Location
We can
select to store the ViewState in one of several locations:
- Session
- Application
- Appglobals
- Cache
Selection
of the receptacle is done in the web-app's Web.config file:
<appSettings>
<add key="ServerSideViewState" value="true"/>
<!--ViewStateStore: session|application|appglobals|cache-->
<add key="ViewStateStore" value="cache"/>
</appSettings>
Using the Base Class
Since each
class needs to inherit System.Web.UI.Page we will inherit this from our base
class, and inherit our base class in all classes:
Instead of
:
public partial class Default : System.Web.UI.Page
{
...
}
The definition of our class now becomes:
using StaticAppGlobals;
public partial class Default : BasePage
{
...
}
Now, since all pages must inherit Page, the base class inherits this:
namespace StaticAppGlobals
{
public class BasePage : System.Web.UI.Page
{
...
}
...
}
... and now, you're off to the races, with C# ViewState management!
Points of Interest
While using the Server Cache method on a heavily-visited site without proper cache management, a server crash can be experienced, so the other methods would be preferable.
I haven't had a chance to work on a SQL implementation, but would love to see one added to this.
I've added this to several projects I've created. One, gave administrators the options to select the storage location from a radio button list. Thus, no re-compiling and the change was instant.
History
None yet...