Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Creating Custom Providers for Enterprise Library : Page 5

When the providers installed with Enterprise Library don't meet your needs, take advantage of the library's pluggable architecture and roll your own.


advertisement
Implementing the Remove Method
To remove a cached item simply involves deleting the "info" and "data" files that contain the item. The Remove method code builds the full path to each file as a String, and then calls the static Delete method of the File class for each one:

protected override void Remove(int storageKey) { String dataFile = Path.Combine(filePath, String.Concat(storageKey.ToString(), dataExtension)); String infoFile = Path.Combine(filePath, String.Concat(storageKey.ToString(), infoExtension)); if (File.Exists(dataFile)) { // delete files File.Delete(dataFile); try { File.Delete(infoFile); } catch {} } else { throw new FileNotFoundException("Cannot remove cached item", dataFile); } }

Note that it first checks whether the "data" file exists, and throws an exception if it does not. This is one of the rules for using the IBackingStore interface (or the BaseBackingStore class). The provider must raise an exception, not only if it fails to remove the item from the backing store, but also if the item is not there—it should be, unless the in-memory cache and backing store have become desynchronized.

Implementing the RemoveOldItem Method
The BaseBackingStore class calls the RemoveOldItem method before adding a new item with an existing key to the cache, or if an exception occurs when adding a new item to the cache. Effectively this ensures that updates to the cached items succeed, failed updates are removed, and all errors raise exceptions to the Cache Manager so that it can maintain synchronization of the in-memory cache and the backing store. The rule for the RemoveOldItem method is that it must not raise an exception if the item specified in the call to this method does not exist:

protected override void RemoveOldItem(int storageKey) { String dataFile = Path.Combine(filePath, String.Concat( storageKey.ToString(), dataExtension)); String infoFile = Path.Combine(filePath, String.Concat( storageKey.ToString(), infoExtension)); try { // delete files File.Delete(dataFile); } catch { } try { File.Delete(infoFile); } catch {} }

Implementing the UpdateLastAccessedTime Method
Each time client code accesses a cached item that carries a SlidingTime expiration, the Cache Manager calls the UpdateLastAccessedTime method of the backing store provider to update the date and time that the item was last accessed. This value is stored in the LastAccessedTime property of the CacheItem class, but the method override just receives a DateTime instance containing the value to set into the cached item.

In the example provider, the code in the UpdateLastAccessedTime method reads the lines from the "info" file as array of String values using the static File.ReadAllLines method, updates the appropriate value in the array with the new DateTime value (as a String), deletes the existing "info" file, and creates a new "info" file using the static File.WriteAllLines method:



protected override void UpdateLastAccessedTime( int storageKey, DateTime timestamp) { String infoFile = Path.Combine(filePath, String.Concat( storageKey.ToString(), infoExtension)); if (File.Exists(infoFile)) { String[] infoData = File.ReadAllLines(infoFile); infoData[1] = timestamp.ToString(); File.Delete(infoFile); File.WriteAllLines(infoFile, infoData); } else { throw new FileNotFoundException( "Cannot find cache info file", infoFile); } }

Implementing the Flush Method
Removing all the cached items when the Cache Manager calls the Flush method is simply a matter of deleting all the "info" and "data" disk files in the cache folder. The code obtains an array of file names for files with the "data" extension, and iterates through the array. For each name, it attempts to delete the "data" and "info" files with that name:

public override void Flush() { String searchString = String.Concat("*", dataExtension); String[] cacheFiles = Directory.GetFiles(filePath, searchString, SearchOption.TopDirectoryOnly); foreach (String cacheFile in cacheFiles) { String dataFile = Path.Combine(filePath, cacheFile); String infoName = String.Concat( Path.GetFileNameWithoutExtension(cacheFile), infoExtension); String infoFile = Path.Combine(filePath, infoName); try { // delete files File.Delete(dataFile); } catch { } try { File.Delete(infoFile); } catch { } } }

Implementing the LoadDataFromStore Method
When an application that uses the Caching Application Block starts, it creates and populates the in-memory cache from the configured backing store. The Cache Manager calls the LoadDataFromStore method in the provider, which must create, populate, and return a HashTable containing all the cached items (without attempting to filter out any that have expired). The key for each item is the integer hash of the cache key, and the value is a CacheItem instance.

The example provider obtains an array of file names for files with the "data" extension, and iterates through the array. For each name, it reads the "info" file and extracts the String cache key name, uses the last access time to generate a DateTime instance, and uses the sliding duration value stored in the third line to create a TimeSpan instance.

Next, the code reads the data for the cached item as an array of bytes using the static File.ReadAllBytes method, and then calls the static SerializationUtility.ToObject method to recreate the cached object:

protected override System.Collections.Hashtable LoadDataFromStore() { Hashtable cacheItems = new Hashtable(); // get a list of cache files String searchString = String.Concat("*", dataExtension); String[] cacheFiles = Directory.GetFiles(filePath, searchString, SearchOption.TopDirectoryOnly); foreach (String cacheFile in cacheFiles) { // read from "info" file // does not support callbacks or priorities - uses standard values String infoName = String.Concat( Path.GetFileNameWithoutExtension(cacheFile), infoExtension); String infoPath = Path.Combine(filePath, infoName); String[] infoData = File.ReadAllLines(infoPath); String itemKey = infoData[0]; DateTime lastAccessed = DateTime.Parse(infoData[1]); TimeSpan slidingDuration = TimeSpan.Parse(infoData[2]); // deserialize object from "data" file Byte[] itemBytes = File.ReadAllBytes(Path.Combine( filePath, cacheFile)); Object itemValue = SerializationUtility.ToObject(itemBytes); ...

Now the code can recreate the original CacheItem instance using its constructor. For simplicity, it assumes a value of Normal for the cache priority, and creates a new SlidingTime expiration instance using the TimeSpan obtained from the "info" file. Finally, it adds the CacheItem to the HashTable, using the hashed integer cache key as the HashTable key, and moves to the next item in the array of cache file names. After adding all the cached items, the method returns the HashTable to the Cache Manager:

... // create CacheItem and add to Hashtable CacheItem item = new CacheItem(lastAccessed, itemKey, itemValue, CacheItemPriority.Normal, null, new SlidingTime(slidingDuration)); cacheItems.Add(itemKey, item); } return cacheItems; }

Author's Note: If you need to support multiple expiration types and different cache priorities, you must adapt the LoadDataFromStore method implementation to retrieve details of the expiration types and the cache priority, and their values from the "information" file. Then you can convert them into the appropriate object types and build the corresponding CacheItem instance.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap