Monday 23 February 2009

Cloning Objects for the Asp.Net Cache when Data Caching

The importance of object cloning to protect data in the Asp.Net cache when Data Caching.

Level: Beginner to Intermediate

Introducing Cloning Objects for the Asp.Net Cache when Data Caching:

Let's imagine you've populated an object in memory and then written the object to the Asp.Net Cache. Do you know that when you read your object from the cache

  MyClass myObject = ( MyClass )HttpRuntime.Cache[ "mykey" ];
Fig.1

you will have a reference to the same object instance that the cache has access to? This means that any changes you make in your object (fig.2) will be seen by all future requests for the same cached object.

  myObject.Description = "My very own description";
Fig.2

Now this isn't necessarily a good thing. Infact, this could be very bad for you because maybe, you would like to cache an object in the Asp.Net Cache, and then preserve the object until it expires. At the same time, new requests for this cached object might want to make slight modifications to it for their own use, so in essence, if we need to preserve data in the Asp.Net Cache yet allow requests modifications - we have to give each one their very own copy of the cached object.

Protecting Data in the Asp.Net Runtime Cache

To protect and preserve cached data an object should be cloned both ways(when inserting into the cache, and when reading from the cache), using a deep copy, and let me stress "DEEP".

Let me show you some code examples I created for this article. I have created a generic CacheList class that will allow a user to add a generic type to a list and provide caching and loading from the cache. I started by creating a helper class and added a method that will allow me to clone a list.

A Helper Method to Clone List

  public static class GenericsHelper
  {
    public static IList<T> CloneList<T>( IList<T> source )
        where T : ICloneable
    {
      IList<T> clone = new List<T>( source.Count );
      foreach ( T t in source )
      {
        clone.Add( ( T )t.Clone( ) );
      }
      return clone;
    }
  }
Fig.3

CacheList: The Generic List Class

Here are the operations available to a client of CacheList:
  • Add( T item ) : void // call to add an item to our list
  • CacheIt( string cacheKey, DateTime absoluteExpiration ) : void // call to insert list into the runtime cache
  • LoadCached( string cacheKey ) : bool // call to load list from the data in the cache and allow mods without affecting cache

Here is an implementation of CacheList:

  public sealed class CacheList<T> where T : ICloneable
  {
private IList<T> _dataList = new List<T>( );
public void Add( T item ) { _dataList.Add( item ); }
public void CacheIt( string cacheKey, DateTime absoluteExpiration ) { // clone the data list IList<T> clone = GenericsHelper.CloneList<T>( _dataList ); // insert into the runtime cache HttpRuntime.Cache.Insert( cacheKey, clone, null, absoluteExpiration, Cache.NoSlidingExpiration ); }
public bool LoadCached( string cacheKey ) { // perform thread-safe read from cache object obj = HttpRuntime.Cache[ cacheKey ]; // if not null clone it, ret. true to indicate clone success if ( obj != null && obj is IList<T> ) { _dataList = GenericsHelper.CloneList<T>( ( IList<T> )obj ); return true; } return false; }
public IList<T> DataList { get { return _dataList; } }
}
Fig.4

My List is Implementing ICloneable

I have placed a constraint on my list which specifies that we can only stored types implementing the ICloneable interface. Let me point out that I'm assuming the implementations of this interface are performing deep copies and not shallow ones.

Use a search engine to search for ICloneable and you will no-doubt find all sorts of discussions about how good or bad it is, and to be honest I can see the arguments against using this interface. In a nutshell developers are suggesting this method isn't clear enough; it doesn't indicate whether a object that implements it is performing a deep or a shallow copy. However, if you agree with the reasons against using it then you don't really have a problem; just your own cloneable interface to write.....and maybe you could make it generic. This is an article by Brad Adams about implementing ICloneable, or not as his case may be.

Will a deep copy affect performance?

This I can't answer for you I'm afraid. I mean, if you are loading and caching a list of lookup values for a drop down list then I would say "don't worry about it". If you're loading ten thousand search results then you might possibly run in to performance issues, but at the same time I'd probably suggest you don't cache this information.

Thanks for reading

Well that's it for the code that preserves our cached data using cloning. This is a pretty simple example and a very simplistic implementation of a list. However, I didn't want to detract from purpose of the article. I'm going to extend this article in future and show you how this sort of cache access can be wrapped in a business/domain object.

I hope you enjoyed the article, there will be many more to come.

No comments:

Post a Comment