For most web developers, one of the breakthroughs that the .net framework brought them is the ViewState. That enabling technology that save/restore page state between postback. But this cool stuff carries with it an overhead. Enabled by default and for the most part ignored during development, the ViewState may bloat to a size that may affect performance especially during page load.
So what do we do?
We can turn viewstate off all together. A not so good idea because that would mean throwing an extra programming effort for you to take care of the page state. Although a nicer idea would be to turn viewstate off for only the controls that need don't need it. But this won't entirely solve the problem of a bloated viewstate.
So what are the other options?
Other option I've seen on the net entails compressing the viewstate. I've tried it and it actually reduced the size of my viewstate by more than a half! Below is my port of it in .NET 2.0 (FYI, .NET 2.0 now comes with a compression namespace but you can always opt to use 3rd party products like ShapNZipLib, ComponentOne.Zip, etc).
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.IO;
using
System.IO.Compression;
public
class GZip
{
public static byte[] Compress(byte[] b)
{
MemoryStream ms = new MemoryStream();
GZipStream zs = new GZipStream(ms, CompressionMode.Compress, true);
zs.Write(b, 0, b.Length);
zs.Close();
return ms.ToArray();
}
public static byte[] Decompress(byte[] b)
{
MemoryStream ms = new MemoryStream();
GZipStream zs = new GZipStream(new MemoryStream(b), CompressionMode.Decompress, true);
byte[] buffer = new byte[4096];
int size;
while (true)
{
size = zs.Read(buffer, 0, buffer.Length);
if (size > 0) ms.Write(buffer, 0, size);
else break;
}
zs.Close();
return ms.ToArray();
}
}
public
class Deflate
{
public static byte[] Compress(byte[] b)
{
MemoryStream ms = new MemoryStream();
DeflateStream zs = new DeflateStream(ms, CompressionMode.Compress, true);
zs.Write(b, 0, b.Length);
zs.Close();
return ms.ToArray();
}
public static byte[] Decompress(byte[] b)
{
MemoryStream ms = new MemoryStream();
DeflateStream zs = new DeflateStream(new MemoryStream(b), CompressionMode.Decompress, true);
byte[] buffer = new byte[4096];
int size;
while (true)
{
size = zs.Read(buffer, 0, buffer.Length);
if (size > 0) ms.Write(buffer, 0, size);
else break;
}
zs.Close();
return ms.ToArray();
}
}
Is that it?
The buck doesn't stop here. I've read Dino Esposito's Programming Microsoft ASP.NET (ISBN:0735619034) and in one of the advance topics in his book he mentioned saving the ViewState in a file at the server. I was having second thoughts on this idea at first. Since this would mean lots of files and disk read/write activities in the server but then I thought of the advantages (and it outweighs the dis in some ways). First, security-wise, the ViewState won't get manipulated. Second, at least you have control of the server's resources and you can scale it up all you want rather than upgrading all of the client workstation. Third, I can't think of anything but I'm sure with this, I'll be saving the client some load up time =). See how it's implemented below:
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Web.UI;
using
System.IO;
public
class ZipPage : Page
{
public ZipPage() : base()
{
}
protected override void OnPreLoad(EventArgs e)
{
this.ViewState["_storeViewStateInServer"] = false;
base.OnPreLoad(e);
}
/// <summary>
/// Sets whether to store ViewState in the server or in the client's browser.
/// Note that ViewState stored in the server will not be compress. Make sure
/// also that the ASP.NET machine account should have proper rights to write
/// the ViewState files in the server's temporary
/// Default value is false.
/// </summary>
public bool StoreViewStateInServer
{
get
{
return (bool)this.Session["_storeViewStateInServer"];
}
set
{
this.Session["_storeViewStateInServer"] = value;
}
}
protected override object LoadPageStateFromPersistenceMedium()
{
LosFormatter f = new LosFormatter();
string vstate;
if (StoreViewStateInServer)
{
StreamReader sr = new StreamReader(GetFileName());
vstate = sr.ReadToEnd();
sr.Close();
}
else
{
vstate = this.Request.Form["__ZIPSTATE"];
byte[] b = Convert.FromBase64String(vstate);
b = Deflate.Decompress(b);
vstate = Convert.ToBase64String(b);
}
return f.Deserialize(vstate);
}
protected override void SavePageStateToPersistenceMedium(object state)
{
LosFormatter f = new LosFormatter();
if (StoreViewStateInServer)
{
StreamWriter sw = new StreamWriter(GetFileName());
f.Serialize(sw, state);
sw.Close();
}
else
{
StringWriter sw = new StringWriter();
f.Serialize(sw, state);
byte[] b = Convert.FromBase64String(sw.ToString());
b = Deflate.Compress(b);
ClientScript.RegisterHiddenField("__ZIPSTATE", Convert.ToBase64String(b));
}
}
private string GetFileName()
{
string url = Request.ServerVariables["Path_Info"];
url = url.Replace("/", "_");
// Place the file in a temp folder (with write permissions)
string fileName = "{0}/{1}_{2}.viewstate";
fileName = String.Format(fileName, "Temp", Session.SessionID, url);
return Server.MapPath(fileName);
}
}
Now you'll just have to inherit from this class and you're on the go. By default the ViewState won't be save on the server, I'll let you set it up by setting the StoreViewStateInServer property. Note that opting to save the ViewState in the server won't compress it since it doesn't matter anyway. Also please don't forget to give ASPNET machine account proper rights to the folder you'll be writing those temp tables or else it won't be able to persist the ViewState and exceptions will occur.
Conclusion
The two solutions cited to reduce the load up time of your aspx page are just one of the many things you can do the tweak you web app performance. Next blog I'll be showing you how to compress the HTML rendered on the page by removing the extra characters inserted by ASP.NET during page rendering.