SYSK 385: Smart Cache

John Manaloto, one of the architects I’ve worked with recently, has been kind enough to allow me to publish the “smart cache” class he’s put into one of his projects… It’s a component built on top of Microsoft Caching Application Block from the enterprise library, but it has an additional capability of actually getting the data if it’s not in cache and adding it to cache for faster access in future get methods, making the whole idea of using the caching component much more transparent.

Here is the code:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Practices.EnterpriseLibrary.Caching;

 

namespace YourCompany.Utilities.Caching

{

    /// <summary>

    /// Generic cache manager.

    /// </summary>

    public interface ISmartCache

    {

        TResult GetItem<T1, TResult>(string key, Func<T1, TResult> getMethod, T1 methodInput, CacheItemPriority scavengingPriority, params ICacheItemExpiration[] expirations);

 

        TResult GetItem<T1, TResult>(string storeName, string key, Func<T1, TResult> getMethod, T1 methodInput, CacheItemPriority scavengingPriority, params ICacheItemExpiration[] expirations);

 

        TResult GetItem<TResult>(string key, Func<TResult> getMethod, CacheItemPriority scavengingPriority, params ICacheItemExpiration[] expirations);

 

        TResult GetItem<TResult>(string storeName, string key, Func<TResult> getMethod, CacheItemPriority scavengingPriority, params ICacheItemExpiration[] expirations);

 

        /// <summary>

        /// Clear all items in the specified storeName.

        /// </summary>

        /// <param name="storeName"></param>

        void Flush(string storeName);

 

        string[] StoreNames { get; }

    }

}

 

 

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using EntLib = Microsoft.Practices.EnterpriseLibrary.Caching;

using Microsoft.Practices.EnterpriseLibrary.Caching;

 

namespace YourCompany.Utilities.Caching

{

    public class SmartCache : ISmartCache

    {

        private static readonly List<string> _storeNames = new List<string>();

 

        #region Methods

 

        private TResult DoGetItem<T1, TResult>(string storeName, string key, Func<T1, TResult> getMethod, T1 methodInput, CacheItemPriority scavengingPriority, params Microsoft.Practices.EnterpriseLibrary.Caching.ICacheItemExpiration[] expirations)

        {

            ICacheManager cache = null;

 

            if (!string.IsNullOrEmpty(storeName))

            {

                cache = CacheFactory.GetCacheManager(storeName);

                lock (_storeNames)

                {

                    if (!_storeNames.Contains(storeName)) _storeNames.Add(storeName);

                }

            }

            else

            {

                cache = CacheFactory.GetCacheManager();

            }

 

            TResult result = (TResult)cache[key];

 

            if (result == null)

            {

                result = getMethod(methodInput);

               

                // Item is overwritten if it is already in the cache.

                cache.Add(key, result, scavengingPriority, null, expirations);

            }

 

            return (TResult)result;

        }

 

        private TResult DoGetItem<TResult>(string key, Func<TResult> getMethod, CacheItemPriority scavengingPriority, params ICacheItemExpiration[] expirations)

        {

            return DoGetItem(null, key, getMethod, scavengingPriority, expirations);

        }

 

        private TResult DoGetItem<TResult>(string storeName, string key, Func<TResult> getMethod, CacheItemPriority scavengingPriority, params ICacheItemExpiration[] expirations)

        {

            ICacheManager cache = null;

 

            if (!string.IsNullOrEmpty(storeName))

            {

                cache = CacheFactory.GetCacheManager(storeName);

                lock (_storeNames)

                {

                    if (!_storeNames.Contains(storeName)) _storeNames.Add(storeName);

                }

            }

            else

            {

                cache = CacheFactory.GetCacheManager();

            }

 

            TResult result = (TResult)cache[key];

 

            if (result == null)

            {

                result = getMethod();

 

                // Item is overwritten if it is already in the cache.

                cache.Add(key, result, scavengingPriority, null, expirations);

            }

 

            return result;

        }

 

        /// <summary>

        /// Remove all items from cache.

        /// </summary>

        /// <param name="storeName"></param>

        public void Flush(string storeName)

        {

            ICacheManager cache = CacheFactory.GetCacheManager();

            if (storeName != null)

            {

                cache = CacheFactory.GetCacheManager(storeName);

            }

 

            if (cache != null)

            {

                lock (cache)

                {

                    cache.Flush();

                }

            }

        }

 

        #endregion Methods

 

        #region ISmartCache

 

        public string[] StoreNames

        {

            get { return _storeNames.ToArray(); }

        }

 

        /// <summary>

        /// Generic retrieval of an item from cache; if not found, item is retrieved from its original source.

        /// </summary>

        /// <typeparam name="T1">Input parameter used when cache must be retrieved from its original source.</typeparam>

        /// <typeparam name="TResult">The type of the item being retrieved.</typeparam>

        /// <param name="storeName">The name of the specific cache instance.</param>

        /// <param name="key">Cache key.</param>

        /// <param name="getMethod">The method used to retrieve item from its original source.</param>

        /// <param name="methodInput"></param>

        /// <param name="scavengingPriority"></param>

        /// <param name="expirations"></param>

        /// <returns></returns>

        public TResult GetItem<T1, TResult>(string storeName, string key, Func<T1, TResult> getMethod, T1 methodInput, CacheItemPriority scavengingPriority, params Microsoft.Practices.EnterpriseLibrary.Caching.ICacheItemExpiration[] expirations)

        {

            return this.DoGetItem(storeName, key, getMethod, methodInput, scavengingPriority, expirations);

        }

 

        /// <summary>

        ///

        /// </summary>

        /// <typeparam name="T1"></typeparam>

        /// <typeparam name="TResult"></typeparam>

        /// <param name="key"></param>

        /// <param name="getMethod"></param>

        /// <param name="methodInput"></param>

        /// <param name="scavengingPriority"></param>

        /// <param name="expirations"></param>

        /// <returns></returns>

        /// <remarks>Use default ICacheManager.</remarks>

        public TResult GetItem<T1, TResult>(string key, Func<T1, TResult> getMethod, T1 methodInput, CacheItemPriority scavengingPriority, params Microsoft.Practices.EnterpriseLibrary.Caching.ICacheItemExpiration[] expirations)

        {

            return this.DoGetItem(null, key, getMethod, methodInput, scavengingPriority, expirations);

        }

 

        public TResult GetItem<TResult>(string storeName, string key, Func<TResult> getMethod, CacheItemPriority scavengingPriority, params ICacheItemExpiration[] expirations)

        {

            var cache = CacheFactory.GetCacheManager(storeName);

            return this.DoGetItem(storeName, key, getMethod, scavengingPriority, expirations);

        }

 

        public TResult GetItem<TResult>(string key, Func<TResult> getMethod, CacheItemPriority scavengingPriority, params ICacheItemExpiration[] expirations)

        {

            return this.DoGetItem(null, key, getMethod, scavengingPriority, expirations);

        }

 

        #endregion

    }

}