Thursday, June 10, 2004

Microsoft Caching Application Blocks

The Microsoft caching application block is very useful building block for developing enterprise .NET Applications. I found this very useful, and it reduces a great deal of coding effort. However, I found this piece of information worth sharing, so that it will be useful for other developers.

There is a FileDependency class which is used to set the cache dependency on a cached file. The as-is logic of the application block will refresh the cache every time the file is Accessed. It uses the following logic.

While an item is added to the cache, the App block, makes a note of the LastAccessTime of the shared file.
The App block has an in built File System Watcher, which will scan the file after every ‘n’ seconds, and will determine.
· If the current “LastAccessTime” of the file is greater than the value, that was determined in step 1 above.

· If yes the app block will refresh the cache.

The problem I faced here is, my application will read the file more frequently and will update the file less frequently. For example, if my application accesses the file (to read certain values) for 10 times in a day, it may update the file only once in a day. So going by the given logic of the App-Block the cache was getting refreshed for all the 10 times. The very purpose of caching was not served.


So I decided to change the code (Microsoft PAG allows us to change and recompile the code). I changed the logic of the FileDependency class, so that it will refresh the cache based on LastUpdated time rather on LastAccess time. The modified code is as below.

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Security.Permissions;

// Used for Handling Exceptions
using Microsoft.ApplicationBlocks.ExceptionManagement;

namespace Microsoft.ApplicationBlocks.Cache.Expirations
{
///
/// This class tracks a file cache dependency.
///

[Serializable]
public class FileDependency : ICacheItemExpiration, ISerializable
{
#region Private members
private string keyValue;
private string dependencyFileName;
private DateTime lastAccessedTime;
#endregion

#region Constructor
///
/// Constructor with one argument.
///

///
/// Indicates the file name of the file
///
public FileDependency( string fullFileName )
{
try
{

string path;
string fileName;
#region Throwing Argument Exception
if(Object.Equals(fullFileName, null))
{
throw new ArgumentNullException("fullFileName",
CacheResources.ResourceManager[RES_ExceptionNullFileName"]);
}
if(fullFileName.Length == 0)
{
throw(new ArgumentOutOfRangeException("fullFileName",
CacheResources.ResourceManager"RES_ExceptionEmptyFileName"]));
}
if(! File.Exists(fullFileName))
{
throw(new ArgumentException(CacheResources.
ResourceManager["RES_ExceptionInvalidFileName"],
"fullFileName"));

}

#endregion

// Validate File
FileInfo fileDependencyInfo = new FileInfo(fullFileName);
if (!fileDependencyInfo.Exists)
{
throw new FileNotFoundException();
}
// Get Path from full file name
path = Path.GetDirectoryName(fullFileName);
// Get file name from full file name
fileName = Path.GetFileName(fullFileName);
dependencyFileName = fullFileName;
//////////////////////////////////////////////////////
//Changed HERE
//////////////////////////////////////////////////////
lastAccessedTime = File.GetLastWriteTime(fullFileName);
//////////////////////////////////////////////////////
/// END OF CHANGE
//////////////////////////////////////////////////////
}
catch( Exception genException )
{
ExceptionManager.Publish( genException );
throw;
}
}
#endregion
#region ICacheItemExpiration Implementation

///
/// Event to indicate the cache item expiration.
///

public event ItemDependencyChangeEventHandler Change;
///
/// This method sets the external dependency key.
///

void ICacheItemExpiration.Key(string keyVal)
{
try
{
#region Throwing Argument Exception
if( Object.Equals(keyVal, null) )
{
throw new ArgumentNullException( "keyVal",
CacheResources.ResourceManager[
"RES_ExceptionNullKey" ] );
}
if( keyVal.Length == 0)
{
throw new ArgumentOutOfRangeException( "keyVal",
CacheResources.ResourceManager[
"RES_ExceptionEmptyKey" ] );
}
#endregion
keyValue = keyVal;
}
catch(Exception genException)
{
ExceptionManager.Publish(genException);
throw;
}
}
///
/// Specifies if the item has expired or not.
///

bool ICacheItemExpiration.HasExpired()
{
try
{
// Compare the Filedependency object's last write time
// value with the last write time value. If they are not
// equal return true, otherwise false.

//////////////////////////////////////////////////////
//Changed HERE
//////////////////////////////////////////////////////
if(DateTime.Compare(lastAccessedTime,
File.GetLastWriteTime(dependencyFileName)) != 0)
{
return true;
}
//////////////////////////////////////////////////////
/// END OF CHANGE
//////////////////////////////////////////////////////
}

catch(Exception genException)
{
ExceptionManager.Publish(genException);
}
return false;
}

///
/// Notifies that the item was recently used.
///

void ICacheItemExpiration.Notify()
{}
#endregion
#region Serialization functions
#region Public method
///
/// This method performs the serialization of members of the
/// current class.
///

///
/// A SerializationInfo object which is deserialized by the
/// formatter and then passed to current constructor
///
///
/// A StreamingContext that describes the source of the
/// serialized stream from where the Serialization object
/// is retrieved
///
[SecurityPermissionAttribute(
SecurityAction.Demand, SerializationFormatter = true)]
[SecurityPermissionAttribute(
SecurityAction.LinkDemand, SerializationFormatter = true)]
public void GetObjectData( SerializationInfo info,
StreamingContext context )
{
// Adds the file name and last accessed time
// into the SerializationInfo,
// where it is associated with the name key
info.AddValue("fileName", dependencyFileName);
info.AddValue("lastAccessedTime", lastAccessedTime);
}
#endregion

#region Constructor
///
/// This method performs the deserialization of members of the
/// current class.
///

///
/// A SerializationInfo object which is deserialized by the
/// formatter and then passed to current constructor
///
///
/// A StreamingContext that describes the source of the
/// serialized stream from where the Serialization object
/// is retrieved
///
protected FileDependency ( SerializationInfo info,
StreamingContext context )
{
try
{
// Getting the value of file name and
// last accessed time
dependencyFileName = info.GetString("fileName");
lastAccessedTime = Convert.ToDateTime(
info.GetValue("lastAccessedTime",
typeof(DateTime)));

}
catch( Exception genException )
{
ExceptionManager.Publish( genException );
}
}
#endregion
#endregion
}
}

0 Comments:

Post a Comment

<< Home