Caching in with Kentico

2
Comments
3
Votes
Login to vote

Caching is an important part of website performance. Kentico has caching built right into its web parts, controls, and site settings making it easy to take advantage of its caching capabilities. You can read in depth about Kentico caching in a post by Martin Hejtmanek (http://devnet.kentico.com/Blogs/Martin-Hejtmanek/April-2009/Deep-Dive---Kentico-CMS-Caching.aspx). In this post I’m going to show you how to utilize Kentico’s caching API in your code. We will then abstract that code into a utility class that will simplify our methods and reduce redundancy. Let’s first draw an example from Kentico Angler using the fish document type.

The first step is to create an object to contain the data of our fish document type:

public class Fish : CMSDocument
{
    public int FishId { get; set; }
    public string FishName { get; set; }
    public string ScientificName { get; set; }
    public string Description { get; set; }
    public string ImagePath { get; set; }
}

Now let’s go fishing by writing the data access code to pull the fish from the Kentico repository.

internal static IEnumerable<Fish> GetFish()
{
    // Get fish from database
    var ds = TreeHelper.SelectNodes("/Fish/%", CMSContext.CurrentSite.CombineWithDefaultCulture, "KenticoAngler.Fish", null, "FishName", 1, true);

    if (DataHelper.DataSourceIsEmpty(ds))
        return null;

    // Map the returned data rows to Fish class and return
    return from DataRow row in ds.Tables[0].Rows select DocMapper.Map<Fish>(row);
}

Now let’s say we only want fish that have pictures, we could write something like this:

public static IEnumerable<Fish> GetFishWithPictures()
{
    var fish = GetFish();

    return fish.Where(x => !string.IsNullOrEmpty(x.ImagePath));
}

Here is a good place to take advantage of caching, we will cache the ‘GetFish’ call to minimize the trips to the database. Now our ‘GetFishWithPictures’ method will look like this:

public static IEnumerable<Fish> GetFishWithPictures()
{
    int cacheMin = CacheHelper.CacheMinutes(CMSContext.CurrentSiteName);
    string key = "AllFish";

    IEnumerable<Fish> fish = null;

    using (var cs = new CachedSection<IEnumerable<Fish>>(ref fish, cacheMin, true, null, key))
    {
        if (cs.LoadData)
        {
            fish = GetFish();
            cs.Data = fish;
        }
    }

    return fish.Where(x => !string.IsNullOrEmpty(x.ImagePath));
}

One immediate problem you’ll notice is that we have just bloated our method from two lines of code to eight. We have also convoluted our method with caching logic. So how can we better code this method? We can abstract the caching implementation into a helper method like this:

public static class CacheUtility
{
    public static T GetCached<T>(Func<T> funcToCache, string key, int? minutes = null)
    {
        int cacheMin = minutes ?? CacheHelper.CacheMinutes(CMSContext.CurrentSiteName);

        T returnValue = default(T);

        using (var cs = new CachedSection<T>(ref returnValue, cacheMin, true, null, key))
        {
            if (cs.LoadData)
            {
                var value = funcToCache.Invoke();
                cs.Data = value;
                return value;
            }

            return returnValue;
        }
    }
}

Here we are taking advantage of the ‘Func<T>’ delegate so that we can pass in any function as a delegate that will only be called if our results are not found in cache first. Now we can rewrite our ‘GetFishWithPictures’ method like this:

public static IEnumerable<Fish> GetFishWithPictures()
{
    var fish = CacheUtility.GetCached(GetFish, "AllFish");

    return fish.Where(x => !string.IsNullOrEmpty(x.ImagePath));
}

We are now back down to two lines of code with caching. With the ‘GetCached’ utility you can also use anonymous methods, so if we wanted to cache the entire method it would look like this:

public static IEnumerable<Fish> GetFishWithPictures()
{
    return CacheUtility.GetCached(() =>
        {
            var fish = GetFish();
            return fish.Where(x => !string.IsNullOrEmpty(x.ImagePath));
        }, "AllFish");
}

In summary we have achieved the following:

  • Simplified our code, keeping it concise and easy to read.
  • Reduced redundancy by not repeating the caching logic, which also protects us from changes to the caching implementation in the future a.k.a. DRY.

To learn more about Kentico’s caching API read this post by Martin Hejtmanek http://devnet.kentico.com/Blogs/Martin-Hejtmanek/June-2010/New-in-5-5--Caching-API.aspx.

Download

You can download the code associated with this post here.

 
Posted by Ryan Williams on 8/26/2011 10:47:57 AM
Filed under: caching
  
Comments
alundgren
I've been architecting a custom caching helper within a Kentico application using ASP.NET's standard cache class, but this is superior, as it uses Kentico's system. Thanks for the tip!

--
Andrew Lundgren
http://www.theatomgroup.com
9/9/2012 9:43:15 PM

Jeroen
Great post Ryan! I really like your generic helper method approach for caching! Keep up the good work :)
9/29/2011 4:06:52 AM