Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ASP.NET Runtime Cache - Clone Objects to Preserve Cached Data

0.00/5 (No votes)
13 Feb 2009 1  
An article about the importance of object cloning if cached data is to be preserved in the .NET cache

Introduction

Let's imagine you've populated an object in memory and then written the object to the ASP.NET runtime 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.

// no so...  I'm afraid every future request will also see this description
myObject.Description = "My very own description"; 
Fig. 2

Now this isn't necessarily a good thing. In fact, this could be very bad for you because maybe, you would like to cache an object, and then preserve the object until it expires from the cache. 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 the cache yet allow request modifications - we have to give each request its very own copy of the object.

So How Do We Protect the Data in the ASP.NET Runtime Cache?

Answer: By cloning the object both ways (when inserting into the cache and 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.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;
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;
    }
}

Then I implemented a simple generic list that will allow me to:

  • // call to add an item to our list 
    Add(T item) : void 
  • // call to insert list into the runtime cache 
    CacheIt(string cacheKey, DateTime absoluteExpiration) : void 
  • // call to load list from the data in the cache 
    // and allow mods without affecting cache 
    LoadCached(string cacheKey) : bool The generic type must implement ICloneable

I have placed a constraint on my list which specifies that we can only stored types that implement 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.

/// <summary>
/// A simple class to demonstrate how we can protect/preserve our data
/// in the cache.
/// </summary>
/// <typeparam name="T">Our generic type</typeparam>

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 this obj is not null then clone it, 
        // returning 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; }
    }
}

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. I will be writing many more articles about C#, ASP.NET and design on my blog dotnet notepad.

Useful Links

History

  • 13th February, 2009: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here