In this section we will implement Scott’s recommendations
for a better solution for storing the view state in a persistent medium.
The property that was used in the above referenced article
to generate the file name is shown in Listing 1 below.
Listing 1
/// <summary>
/// The path for this user's/page's view state information.
/// </summary>
public string ViewStateFilePath
{
get
{
string folderName = Path.Combine(Request.PhysicalApplicationPath,
"PersistedViewState");
string fileName = Session.SessionID + "-" +
Path.GetFileNameWithoutExtension(Request.Path).Replace("/", "-") + ".vs";
return Path.Combine(folderName, fileName);
}
}
The enhancements on the above property are shown in Listing
2.
Listing 2
/// <summary>
/// The path for this user's/page's view stateinformation.
/// </summary>
public string ViewStateFilePath
{
get
{
string folderName = Path.Combine(Request.PhysicalApplicationPath,
"PersistedViewState");
string _FileName = VsKey + "-" + Path.GetFileNameWithoutExtension
(Request.Path).Replace("/", "-") + ".vs";
return Path.Combine(folderName, fileName);
}
}
You can notice that we have replaced the Session.SessionID
with the VsKey. VsKey is nothing but a public property that is used to
retrieve the GUID created and stored in a hidden field in the page.
The hidden field is added to the page as follows.
Listing 3
<asp:HiddenField ID="vsKey"runat="server" />
The VsKey property is shown in Listing 4.
Listing 4
/// <summary>
/// Stores the HiddenField key representing the GUID-FileName of the ViewState of the current page
/// </summary>
public string VsKey
{
get
{
HiddenField _VsHiddenKey = null;
string _VsKey = "";
// Get the HiddenField Key from the page
_VsHiddenKey = FindControlRecursive(this, "vsKey")as HiddenField;
// Get the HiddenField value from the page
string _VsHiddenKeyValue = GetControlValue(_VsHiddenKey.UniqueID.ToString());
if (_VsHiddenKeyValue != null)
{
_VsKey = _VsHiddenKeyValue;
}
// First time access, generate a key for the ViewState file
if (!Page.IsPostBack)
{
_VsKey = GenerateGUID();
_VsHiddenKey.Value = _VsKey;
}
// Return the VS key
return _VsKey;
}
}
The first time this property is accessed, a new GUID is
created and assigned to the value property of the Hidden field and then used to
construct the file name to hold the view state of the current page being
executed.
On a post back, the GUID will be retrieved from the hidden
field on the page and will be used to construct the file where the view state
is stored. Then a simple access to read the text file is done and the page’s
view state is there!
The first improvement as Scott specified is accomplished.
Now we need a mechanism to help us clean up the file system
from the view state files that are no longer needed.
I decided to add a key to the AppSettings section in the
web.config as follows:
Listing 5
<!-- specifies the days before which we shoulddelete the ViewState -->
<add key="vsDays"value="1"/>
The algorithm works as follows. Inside the
LoadPageStateFromPersistentMedium() method implementation given by Scot, I will
add a call to a new method called CleanupFiles(). This method is shown in
Listing 6.
Listing 6
/// <summary>
/// Clean up files to gain some space
/// </summary>
private void CleanupFiles()
{
try
{
// Create a reference to the folder holding all viewstate files
string _FolderName = Path.Combine(WebUtils.PhysicalAppPath,
"PersistedViewState");
// Create a reference to the VS directory
DirectoryInfo _Directory = new DirectoryInfo(_FolderName);
// Create an array representing the files in the current directory.
FileInfo[]_Files = _Directory.GetFiles();
// Delete all files whose CreationTime is > Xdays.
// By default its 7 days, 1 week.
DateTime _Threshold = DateTime.Now.AddDays( - 7);
if (WebConfigurationManager.AppSettings["vsDays"] != null)
{
if(WebConfigurationManager.AppSettings["vsDays"].ToString() != "")
{
int _vsDays = Convert.ToInt32(WebConfigurationManager.AppSettings[
"vsDays"].ToString()) * ( - 1);
_Threshold = DateTime.Now.AddDays(_vsDays);
}
}
foreach (FileInfo _File in _Files)
{
if (_File.CreationTime <= _Threshold)
_File.Delete();
}
}
catch (Exception ex)
{
throw new ApplicationException("CleanupFiles in BasePage");
}
}
This method will read the vsKey in the web.config. The
vsKey represents the number of days that will be subtracted from today’s date,
thus forming a DateTime threshold. Each view state file’s creation date will
be compared to that threshold; any file whose creation date is less than or
equal to that threshold will be deleted. This way we will be freeing up some
memory space to hold new view state files.
You can check the updated
LoadPageStateFromPersistentMedium() method in which we specify a call to the
method responsible for cleaning up the files from the file system.
Listing 7
/// <summary>
/// Loads the page's view state from the Webserver's file system.
/// </summary>
protected override object LoadPageStateFromPersistenceMedium()
{
// determine the file to access
if (!File.Exists(ViewStateFilePath))
return null;
else
{
// Remove all files that have been there for X days!
CleanupFiles();
// open the file
StreamReader _StreamReader = File.OpenText(ViewStateFilePath);
string viewStateString = _StreamReader.ReadToEnd();
_StreamReader.Close();
// deserialize the string
LosFormatter _LostFormatter = new LosFormatter();
return _LostFormatter.Deserialize(viewStateString);
}
}
As a result, we have implemented two improvements Scott
mentioned in his article to make the solution he proposed to store the view
state into a persistent medium- a better and robust solution. The improvements
are:
·
Use a GUID value to construct the view state file name per page.
·
A Hidden field is used to keep track of each page’s GUID.
A simple technique is added to clean up unused view state
files to save memory disk space.