.NET Baby Steps: Part VII - Caching

Photo296 Note: This post is part of a series and you can find the rest of the parts in the series index.

.NET has had one out-of-the-box way to do caching in the past, System.Web.Caching. While a good system, it suffered from two issues. Firstly, it was not extensible, so if you wanted to cache to disk or SQL or anywhere other than memory, you were out of luck. Secondly, it was part of ASP.NET and while you could use it in WinForms, it took a bit of juggling.

The patterns & practices team saw these issues and provided a caching application block in their Enterprise Library which has been used by everyone who did not want to reinvent the wheel. Thankfully, from .NET 4 there is a caching system now included in the framework that solves those two issues. This is known as System.Runtime.Caching.

Slow Example

To see how to use it, let’s start with a process we can cache. I have a class called Demo which has a property named Times of type IEnumerable<DateTime>. To set the value of Times, you call the SetTimes method, which populates the property with five values. However, there is a delay of 500ms between each addition of a DateTime to the Times property, so it takes 2.5 seconds to run. In my Program class, I have a method, PrintTimes, which creates a new Demo object, calls SetTimes, and then prints the value to the screen. Lastly, in my Main method, I call PrintTimes three times—in total, it takes 7.5 seconds to run.

class Program
{
    public static void Main()
    {
        PrintTimes();
        PrintTimes();
        PrintTimes();
    }

    private static void PrintTimes()
    {
        Demo demo = new Demo();
        Stopwatch stopwatch = Stopwatch.StartNew();
        demo.SetTimes();
        foreach (DateTime time in demo.Times)
        {
            Console.WriteLine(time);
        }
        stopwatch.Stop();
        Console.WriteLine("It took {0} to print out the times", stopwatch.ElapsedMilliseconds);
    }
}

class Demo
{
    public List<DateTime> Times { get; set; }

    public void SetTimes()
    {
        if (Times == null)
        {
            Times = new List<DateTime>();
            for (int counter = 0; counter < 5; counter++)
            {
                Thread.Sleep(500);
                Times.Add(DateTime.Now);
            }
        }
    }
}

The output from this example code is below. Note that the times printed are constantly changing, and it takes ~2500ms to print out each set of values.

image

Cache Example

Now I modify the PrintTimes method to incorporate caching by creating an ObjectCache that uses the default MemoryCache instance. I can check if the cache contains an object using the .Contains method, retrieve it with .Get, and add to it using .Add:

private static void PrintTimes()
{
    Demo demo;

    Stopwatch stopwatch = Stopwatch.StartNew();

    ObjectCache cache = MemoryCache.Default;
    if (cache.Contains("demo"))
    {
        demo = (Demo)cache.Get("demo");
    }
    else
    {
        demo = new Demo();
        demo.SetTimes();
        cache.Add("demo", demo, new CacheItemPolicy());
    }

    foreach (DateTime time in demo.Times)
    {
        Console.WriteLine(time);
    }
    stopwatch.Stop();
    Console.WriteLine("It took {0} to print out the times", stopwatch.ElapsedMilliseconds);
}

This gives the output:

image

Note that the time to print for the first iteration is about 60ms longer, but the next two take 0ms, and the values in the three sets are the same.

CacheItemPolicy

In the above example, I only use a default CacheItemPolicy, but you can do a lot more with it:

Things to Watch Out For