Introduction
This article is strengthening a previous article I've written, ASP.NET Runtime Cache - Clone Objects to Preserve Cached Data, by presenting you with better code that is available for you to download from this page and use in your applications.
Allow me to re-iterate. My previous article explained that the ASP.NET runtime cache is shared between website requests and that the cache is unprotected, which means that a user can request an object in the cache and then modify the object's data. A second user can make a request for the same cached object and get my modifications, or worse still, half of my modifications.
Pre-Requisites
Knowledge of the ASP.NET technology, an understanding of data caching, an understanding of shallow vs. deep copying, and knowledge of .NET generics is required for this article.
Lock the Cache
One method of resolving this conflict is to introduce locking around the cache, but this can be expensive, certainly when a system is under heavy load. Threads will get blocked, and the OS will be spending more time synchronizing processors and thread-switching, which can be a major bottleneck.
Deep Copy Our Objects
A alternative, and my preference, is to deep copy objects going in and objects coming out. This way, every request reading the cache is getting its own copy of the objects.
Contents of IDeepCloneable.cs
namespace ProtectedCache {
public interface IDeepCloneable {
object DeepClone();
}
}
Contents of CacheArgs.cs
using System;
using System.Web.Caching;
namespace ProtectedCache {
public sealed class CacheArgs {
private bool _isCacheable;
private DateTime _absoluteExpiration = Cache.NoAbsoluteExpiration;
private TimeSpan _slidingExpiration = Cache.NoSlidingExpiration;
public CacheDependency Dependency {
get {
return null; }
}
public DateTime AbsoluteExpiration {
get { return _absoluteExpiration; }
set { _absoluteExpiration = value; }
}
public TimeSpan SlidingExpiration {
get { return _slidingExpiration; }
set { _slidingExpiration = value; }
}
}
}
Contents of Cacher.cs
using System;
using System.Web;
using System.Collections.Generic;
namespace ProtectedCache {
public static class Cacher {
public static T Load<t>(string key) where T : IDeepCloneable {
object item = HttpRuntime.Cache[key];
if (item != null && (item.GetType() == typeof(T))) {
return (T)((IDeepCloneable)item).DeepClone();
} else {
return default(T);
}
}
public static void Save<t>(T item, string key, CacheArgs args) where T :
IDeepCloneable {
HttpRuntime.Cache.Insert(key, item.DeepClone(), args.Dependency,
args.AbsoluteExpiration, args.SlidingExpiration);
}
}
}
Resources
I try to write when I can, but sometimes it's difficult finding time to complete lengthy articles. However, I do write snippy-snappy posts, providing useful tips and code samples at http://designcodetest.blogspot.com/. Come along, have a read, and please leave feedback.
History
This is the first version of my article.