Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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