Skip to content

Instantly share code, notes, and snippets.

@agehlot
Created March 14, 2021 04:22
Show Gist options
  • Save agehlot/d05ad67ca0dddadd89307ba302aa7d26 to your computer and use it in GitHub Desktop.
Save agehlot/d05ad67ca0dddadd89307ba302aa7d26 to your computer and use it in GitHub Desktop.
Sitecore custom cache class
using Sitecore.Caching;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Core.Foundation.Caching.CustomCaching
{
/// <summary>
/// Class CustomCache.
/// </summary>
public class CustomCache
{
/// <summary>
/// The non publish key
/// </summary>
private const string NonPublishKey = "DontPublish-{0}";
/// <summary>
/// The site key
/// </summary>
private const string SiteKey = "{0}-{1}-SiteCache";
/// <summary>
/// The global key
/// </summary>
private const string GlobalKey = "Global";
// The locking approach to keep the system from throwing threading issues
/// <summary>
/// The cache lock
/// </summary>
private static readonly object _cacheLock = new object();
/// <summary>
/// The caching collection singleton to hold the caching references
/// </summary>
private static readonly Dictionary<string, ICache> CacheCollection = new Dictionary<string, ICache>();
/// <summary>
/// The use custom cache
/// </summary>
private static readonly bool UseCustomCache = Sitecore.Configuration.Settings.GetBoolSetting("CustomCaching.Enabled", true);
/// <summary>
/// Gets the base key.
/// </summary>
/// <param name="isGlobal">if set to <c>true</c> [is global].</param>
/// <param name="siteName">Name of the site.</param>
/// <param name="databaseName">Name of the database.</param>
/// <returns>System.String.</returns>
private static string GetBaseKey(bool isGlobal = false, string siteName = "", string databaseName = "")
{
// the site name can be overridden
if (string.IsNullOrEmpty(siteName))
{
siteName = Sitecore.Context.Site == null ? "NoSite" : Sitecore.Context.Site.Name;
}
// the database can be overridden
if (string.IsNullOrEmpty(databaseName))
{
databaseName = Sitecore.Context.Database == null ? "NoName" : Sitecore.Context.Database.Name;
}
// are we on the global cache
string cacheKey = string.Format(SiteKey, isGlobal ? GlobalKey : siteName, databaseName);
return cacheKey;
}
/// <summary>
/// The easy way to fetch the cache in a locked way
/// </summary>
/// <param name="isGlobal">Is Global</param>
/// <param name="siteName">Name of the site.</param>
/// <param name="databaseName">Name of the database.</param>
/// <returns>Returns the sitecore cache instance</returns>
public static ICache SitecoreCache(bool isGlobal = false, string siteName = "", string databaseName = "")
{
// sets the default cache key
string cacheKey = GetBaseKey(isGlobal, siteName, databaseName);
ICache cache;
// we need to lock the cache due to multi threaded features
lock (_cacheLock)
{
// data found
if (CacheCollection.ContainsKey(cacheKey))
{
// fetch the cache from the collection
cache = CacheCollection[cacheKey];
}
else
{
// fetches from the settings, but has a default size
cache = CacheManager.GetNamedInstance(cacheKey, Sitecore.StringUtil.ParseSizeString(Sitecore.Configuration.Settings.GetSetting("Caching.CacheSize", "100MB")), true);
try
{
// add new reference to the singleton
CacheCollection.Add(cacheKey, cache);
}
catch (Exception ex)
{
Log.Error("Error adding cacheKey " + cacheKey, ex, typeof(CustomCache));
}
}
}
return cache;
}
/// <summary>
/// Gets the cached item.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="cacheKey">The cache key.</param>
/// <param name="globalCache">if set to <c>true</c> [global cache].</param>
/// <param name="siteName">Name of the site.</param>
/// <param name="databaseName">Name of the database.</param>
/// <returns>T.</returns>
public static T GetCachedItem<T>(string cacheKey, bool globalCache = false, string siteName = "", string databaseName = "")
{
// no key so return error
if (string.IsNullOrEmpty(cacheKey))
{
return default(T);
}
object cachedItem = GetCachedItem(cacheKey, globalCache, siteName, databaseName);
if (cachedItem is T)
{
return (T)cachedItem;
}
else
{
return default(T);
}
}
/// <summary>
/// Gets the cached item.
/// </summary>
/// <param name="cacheKey">The cache key.</param>
/// <param name="globalCache">if set to <c>true</c> [global cache].</param>
/// <param name="siteName">Name of the site.</param>
/// <param name="databaseName">Name of the database.</param>
/// <returns>System.Object.</returns>
public static object GetCachedItem(string cacheKey, bool globalCache = false, string siteName = "", string databaseName = "")
{
// no key so return error
if (string.IsNullOrEmpty(cacheKey))
{
return null;
}
object cachedItem = null;
// produce the valid key we want to use
string key = cacheKey.ToLower();
// get the cache we are looking for
ICache cache = SitecoreCache(globalCache, siteName, databaseName);
if (cache != null)
{
// make sure the system has the key before doing anything
if (cache.ContainsKey(key))
{
// get the data from the sitecore cache
cachedItem = cache[key];
}
else
{
// the item might be a non publish key
key = string.Format(NonPublishKey, key).ToLower();
// get the data if found
if (cache.ContainsKey(key))
{
// get the data from the sitecore cache
cachedItem = cache[key];
}
}
}
// return the data or null
return cachedItem;
}
/// <summary>
/// Determines whether the specified cache key contains key.
/// </summary>
/// <param name="cacheKey">The cache key.</param>
/// <param name="globalCache">if set to <c>true</c> [global cache].</param>
/// <param name="siteName">Name of the site.</param>
/// <param name="databaseName">Name of the database.</param>
/// <returns>System.Object.</returns>
public static object ContainsKey(string cacheKey, bool globalCache = false, string siteName = "", string databaseName = "")
{
// no key so return error
if (string.IsNullOrEmpty(cacheKey))
{
return false;
}
// produce the valid key we want to use
string key = cacheKey.ToLower();
// get the cache we are looking for
ICache cache = SitecoreCache(globalCache, siteName, databaseName);
return cache != null && cache.ContainsKey(cacheKey);
}
/// <summary>
/// Cacheds the value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="cacheKey">The cache key.</param>
/// <param name="func">The function.</param>
/// <returns>T.</returns>
public static T CachedValue<T>(string cacheKey, Func<T> func = null)
{
return CachedValue<T>(null, cacheKey, func);
}
/// <summary>
/// Cacheds the value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item">The item.</param>
/// <param name="func">The function.</param>
/// <returns>T.</returns>
public static T CachedValue<T>(Item item, Func<T> func = null)
{
return CachedValue<T>(item, string.Empty, func);
}
/// <summary>
/// Cacheds the value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item">The item.</param>
/// <param name="cacheKey">The cache key.</param>
/// <param name="func">The function.</param>
/// <returns>T.</returns>
public static T CachedValue<T>(Item item, string cacheKey, Func<T> func = null)
{
return CachedValue<T>(item == null ? Guid.Empty : item.ID.Guid, cacheKey, func);
}
/// <summary>
/// Cacheds the value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item">The item.</param>
/// <param name="cacheKey">The cache key.</param>
/// <param name="func">The function.</param>
/// <returns>T.</returns>
public static T CachedValue<T>(Guid item, string cacheKey, Func<T> func = null)
{
var key = item + cacheKey;
var cachedData = GetCachedItem<T>(key);
if (!Equals(cachedData, default(T))) return cachedData;
if (func != null)
{
cachedData = func.Invoke();
}
cachedData = SaveCachedItem(key, cachedData);
return cachedData;
}
/// <summary>
/// Cacheds the value.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TU">The type of the tu.</typeparam>
/// <param name="item">The item.</param>
/// <param name="field">The field.</param>
/// <returns>T.</returns>
public static T CachedValue<T, TU>(TU item, Expression<Func<TU, T>> field) where TU : Item
{
string cacheKey = string.Empty;
var ue = field.Body as UnaryExpression;
if (ue != null)
{
var memberExpression = ue.Operand as MemberExpression;
if (memberExpression != null)
{
cacheKey = memberExpression.Member.Name;
}
}
var body = field.Body as MemberExpression;
if (body != null)
{
cacheKey = body.Member.Name;
}
if (string.IsNullOrWhiteSpace(cacheKey))
{
cacheKey = field.ToString();
}
return string.IsNullOrWhiteSpace(cacheKey) ?
field.Compile()(item) :
CustomCache.CachedValue(item.ID.Guid, cacheKey, () => field.Compile()(item));
}
/// <summary>
/// Saves the list to the cache, if html caching is enabled
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="cacheKey">The unique key to save</param>
/// <param name="cachingData">The data to cache</param>
/// <param name="cacheTimer">The time we want to cache this data</param>
/// <param name="isNoSlidingExpiration">Is the cacheTimer an Absolute Expiration (default) or a sliding expiration</param>
/// <param name="globalCache">Is the data to be stored in the global cache or site specific cache</param>
/// <param name="removeOnPublish">Remove the contents on a publish, this is defaulted as true</param>
/// <param name="siteName">Force set the site name, if this is run from a scheduled task this should be set</param>
/// <param name="databaseName">Force the database if this is run from a scheduled tasks, this should be set</param>
/// <param name="cacheDep">Any caching dependencies for the cache. NOTE: Not valid for Sitecore Caching</param>
/// <param name="priority">The priority of the cache. NOTE: Not valid for Sitecore Caching</param>
/// <param name="callback">The call-back function of the cache. NOTE: Not valid for Sitecore Caching</param>
/// <returns>The object to be cached, regardless of if it was cached or caching is enabled.</returns>
/// .
public static T SaveCachedItem<T>(string cacheKey, T cachingData,
object cacheTimer = null,
bool isNoSlidingExpiration = true,
bool globalCache = false,
bool removeOnPublish = true,
string siteName = "",
string databaseName = "",
System.Web.Caching.CacheDependency cacheDep = null,
System.Web.Caching.CacheItemPriority priority =
System.Web.Caching.CacheItemPriority.Normal,
System.Web.Caching.CacheItemRemovedCallback callback = null)
{
// make sure we have data and caching is enabled
if (string.IsNullOrWhiteSpace(cacheKey) || Equals(cachingData, default(T)) || Sitecore.Context.Site == null || !UseCustomCache)
return cachingData;
// set the key so we can override it
string key = cacheKey.ToLower();
if (!removeOnPublish)
{
key = string.Format(NonPublishKey, key).ToLower();
}
ICache cache = SitecoreCache(globalCache, siteName, databaseName);
//Evict old data from cache
if (cache.ContainsKey(key))
{
cache.Remove(key);
}
if (cacheTimer == null)
{
//Don't worry about the expiration
cache.Add(key.ToLower(), cachingData);
}
else
{
// setup defaults for caching types
TimeSpan slidingCache = System.Web.Caching.Cache.NoSlidingExpiration;
DateTime absoluteCache = System.Web.Caching.Cache.NoAbsoluteExpiration;
// set the cache type
if (isNoSlidingExpiration)
{
// make sure it's right
if (cacheTimer is DateTime)
{
absoluteCache = (DateTime)cacheTimer;
}
else
{
// we have an issue fix up
TimeSpan timeSpanCheck = (TimeSpan)cacheTimer;
absoluteCache = DateTime.Now.Add(timeSpanCheck);
}
}
else
{
// make sure it's right
if (cacheTimer is TimeSpan)
{
slidingCache = (TimeSpan)cacheTimer;
}
else
{
// we have an issue fix up
DateTime dateCheck = (DateTime)cacheTimer;
slidingCache = dateCheck.Subtract(DateTime.Now);
}
}
// use the sitecore cache
cache.Add(key.ToLower(), cachingData, slidingCache, absoluteCache);
}
return cachingData;
}
/// <summary>
/// Removes the required item from the cache
/// </summary>
/// <param name="cacheKey">The cache key</param>
/// <param name="globalCache">Are we using the global cache</param>
/// <param name="siteName">The sitename</param>
/// <param name="databaseName">The database name</param>
/// <returns>Returns true if the data was removed from the cache or false if it wasnt or there was an error</returns>
public static bool RemoveCacheItem(string cacheKey, bool globalCache = false, string siteName = "", string databaseName = "")
{
// no key so return error
if (string.IsNullOrEmpty(cacheKey))
{
return false;
}
// produce the valid key we want to use
string key = cacheKey.ToLower();
// get the cache we are looking for
ICache cache = SitecoreCache(globalCache, siteName, databaseName);
if (cache != null)
{
// make ure the system has the key before doing anything
if (cache.ContainsKey(key))
{
// remove the cached object
cache.Remove(key);
}
else
{
// the item might be a non publish key
key = string.Format(NonPublishKey, key).ToLower();
// get the data if found
if (cache.ContainsKey(key))
{
// remove the cached object
cache.Remove(key);
}
}
}
// data removed
return true;
}
/// <summary>
/// Clears the cache based on the details provided
/// </summary>
/// <param name="siteName">The name of the site to clear it's cached data</param>
/// <param name="databaseName">Name of the database.</param>
/// <param name="globalCache">Clear the global cache as well</param>
/// <param name="removeOnPublish">Remove the data which was indicated as not to be cleared when publishing</param>
public static void ClearCache(string siteName = "", string databaseName = "", bool globalCache = false, bool removeOnPublish = false)
{
// get the cache from sitecore
ICache cache = SitecoreCache(globalCache, siteName, databaseName);
// make sure we have the data
if (cache == null)
{
Log.Debug("Cache not found, looking for:" + siteName, typeof(CustomCache));
return;
}
Log.Debug("Clearing Custom cache: " + cache.Name, typeof(CustomCache));
// process the keys
foreach (string key in cache.GetCacheKeys())
{
// can we remove the item
if (removeOnPublish || !key.Contains("DontPublish-"))
{
// remove the key
cache.Remove(key);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment