Extension methods on Tridion.ContentManager.Templating.PublishingContext to allow caching of items between render actions in a publish transaction
using System; | |
using System.Collections.Generic; | |
using System.Runtime.Caching; | |
using Tridion.ContentManager.Templating; | |
namespace Tridion.Extensions.Templating | |
{ | |
/// <summary> | |
/// Extension methods on the Tridion.ContentManager.Templating.PublishingContext class to allow | |
/// for caching of arbitrary objects, package items and rendered output between | |
/// Render actions within a publish transaction: | |
/// - Sharing information between Page and Component Template when publishing a Page | |
/// - Sharing information between multiple Dynamic CTs when publishing a Component | |
/// - Sharing information between multiple Pages when publishing a Structure Group | |
/// </summary> | |
public static class PublishTransactionCache | |
{ | |
private static MemoryCache _cache; | |
#region Basic Cache Operations on all objects | |
/// <summary> | |
/// Get the Cache Dictionary for the current publishing transaction | |
/// </summary> | |
/// <param name="context">The current publishing context (typically from the Tridion.ContentManager.Templating.Engine)</param> | |
/// <returns>The entire set of cached objects for this publishing transaction</returns> | |
public static Dictionary<String, Object> GetCache(this PublishingContext context) | |
{ | |
//Only use cache if there is a publish transaction (so not for preview, fast track publish and template builder etc) | |
if (context.RenderContext != null && context.RenderContext.PublishTransaction != null) | |
{ | |
String cacheKey = "publish-cache-" + context.RenderContext.PublishTransaction.Id.ItemId; | |
if (_cache == null) | |
{ | |
_cache = new MemoryCache("TridionPublishingCache"); | |
} | |
if (!_cache.Contains(cacheKey)) | |
{ | |
//Create a cache for this publish transaction valid for 5 minutes | |
_cache.Add(cacheKey, new Dictionary<String, Object>(), DateTimeOffset.UtcNow.AddMilliseconds(300000)); | |
} | |
return (Dictionary<String, Object>)_cache[cacheKey]; | |
} | |
return null; | |
} | |
/// <summary> | |
/// Read an object from the cache | |
/// </summary> | |
/// <typeparam name="T">The type of the object expected</typeparam> | |
/// <param name="context">The current publishing context</param> | |
/// <param name="cacheKey">The cache key of the object to read</param> | |
/// <returns>The cached object (or null if not found)</returns> | |
public static T ReadFromCache<T>(this PublishingContext context, String cacheKey) where T : class | |
{ | |
var cache = context.GetCache(); | |
if (cache != null && cache.ContainsKey(cacheKey)) | |
{ | |
var data = cache[cacheKey]; | |
if (data.GetType() == typeof(T)) | |
{ | |
return data as T; | |
} | |
else | |
{ | |
throw new InvalidCastException(string.Format("Data in cache is not of expected type. Data is of type {0}, expected type {1}", data.GetType(), typeof(T))); | |
} | |
} | |
return null; | |
} | |
/// <summary> | |
/// Add an object to the cache | |
/// </summary> | |
/// <param name="context">The current publishing context</param> | |
/// <param name="cacheKey">The cache key of the object to add</param> | |
/// <param name="data">The object to cache</param> | |
public static void AddToCache(this PublishingContext context, String cacheKey, object data) | |
{ | |
var cache = context.GetCache(); | |
if (cache != null) | |
{ | |
if (cache.ContainsKey(cacheKey)) | |
{ | |
cache.Remove(cacheKey); | |
} | |
cache.Add(cacheKey, data); | |
} | |
} | |
/// <summary> | |
/// Remove an item from the cache | |
/// </summary> | |
/// <param name="context">The current publishing context</param> | |
/// <param name="cacheKey">The cache key of the item to remove</param> | |
public static void RemoveFromCache(this PublishingContext context, String cacheKey) | |
{ | |
var cache = context.GetCache(); | |
if (cache != null) | |
{ | |
if (cache.ContainsKey(cacheKey)) | |
{ | |
cache.Remove(cacheKey); | |
} | |
} | |
} | |
#endregion | |
#region Rendered Output Caching | |
/// <summary> | |
/// Check if we have cached output for the item currently being rendered, and if so add it to the package | |
/// If not, optionally add the current Output item to the cache | |
/// </summary> | |
/// <param name="context">Current Publishing Context</param> | |
/// <param name="package">Current Templating Package</param> | |
/// <param name="addCurrentOutputToCache">If true, and no cached output found, the current package Output will be added to the cache</param> | |
/// <returns>true if cached output added to package</returns> | |
public static bool PushOutputFromCache(this PublishingContext context, Package package, bool addCurrentOutputToCache) | |
{ | |
var key = "CachedOutput-" + context.ResolvedItem.Item.Id.ToString(); | |
var cacheIndicator = package.GetValue(key); | |
if (!String.IsNullOrEmpty(cacheIndicator)) | |
{ | |
//Cached Output already in the package from a previous TBB - nothing to do | |
return true; | |
} | |
if (context.PushFromCache(package, key)) | |
{ | |
//Cached output has been pushed, add an indicator so we don't repeatedly do this in other TBBs | |
package.PushItem(key, package.CreateStringItem(ContentType.Text, "Cached Output Added")); | |
return true; | |
} | |
else | |
{ | |
//Output not in Cache, (optionally) cache it for the next similar template | |
if (addCurrentOutputToCache) | |
{ | |
var packageItemName = "Cached" + Package.OutputName; | |
var output = package.GetByName(Package.OutputName); | |
if (output!=null) | |
{ | |
var items = new Dictionary<string, Item>(); | |
items.Add(packageItemName, output); | |
context.PushAndAddToCache(package, items, key); | |
} | |
} | |
return false; | |
} | |
} | |
public static void SwitchToCachedOutput(this Engine engine, Package package) | |
{ | |
var packageItemName = "Cached" + Package.OutputName; | |
var cachedOutput = package.GetByName(packageItemName); | |
if (cachedOutput!=null) | |
{ | |
package.PushItem(Package.OutputName, cachedOutput); | |
} | |
} | |
#endregion | |
#region Caching of Package Items | |
/// <summary> | |
/// Push a dictionary of package items into the package, and store them in the cache for future use | |
/// </summary> | |
public static void PushAndAddToCache(this PublishingContext context, Package package, Dictionary<string, Item> items, string cacheKey) | |
{ | |
if (items != null && items.Count>0) | |
{ | |
foreach (string key in items.Keys) | |
{ | |
package.PushItem(key, items[key]); | |
} | |
context.AddToCache(cacheKey, items); | |
} | |
} | |
/// <summary> | |
/// Load package items from the cache and push them into the package. | |
/// </summary> | |
/// <returns>false if nothing is found in the cache, true otherwise</returns> | |
public static bool PushFromCache(this PublishingContext context, Package package, string cacheKey) | |
{ | |
bool addedItems = false; | |
var items = context.ReadFromCache<Dictionary<string, Item>>(cacheKey); | |
if (items != null) | |
{ | |
foreach (string key in items.Keys) | |
{ | |
package.PushItem(key, items[key]); | |
addedItems = true; | |
} | |
} | |
return addedItems; | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment