Nickoli previously wrote a post introducing Context Variables, with nice Custom Functions to get and set them from your Dreamweaver Templates. Â This post discusses the scope of Context Variables, and how that leads to other useful applications like caching.
For those who have not used them, the SDL Tridion Templating API makes available a dictionary that can be used to temporarily store variables when publishing items – Context Variables.
So the first question is: Where do I find this Dictionary? The answer is to look in the engine object that is passed as one of the parameters to the Transform method.
Dictionary variables = engine.PublishingContext.RenderContext.ContextVariables;
The next question is, What is the scope of these variables – if I put a variable in, when can I take it out?
It turns out that the scope is that of the whole publishing transaction. This means that if I publish a Page, the Page Template and Component Templates share the same set of Context Variables. If I publish a Structure Group, then all Pages rendered share the same Context Variables. If I publish a Component or Page template, all CPs or Pages rendered share the same Context Variables.
This means we have a cache that is valid for the duration of a publishing transaction. For example, supposing I have some configuration which is required for all of my templates (perhaps a set of static text labels, a date format, the current language or locale). In my template code I can add a context variable with the configuration data, and then read this out in any further processing that is done as part of the same publishing transaction without having to load the configuration component, or access the publication metadata or wherever the configuration was stored.
So is this really worth doing? If we are publishing a single Component Presentation there is little benefit, but if we publish a page containing 10 CPs, we save ourself 10 times doing the same processing. If we publish a Structure Group containing 100 such pages, we save 1000 times (and so on). Tridion has its own internal caching of objects during the scope of a Publish Transaction and the rendering of template itself is likely to be more intensive than any configuration reading, but given that it is simple to implement such caching (especially with my helper functions below), it is probably worth doing – especially if you are doing some time-consuming calculation (for example recursively checking parent Structure Group metadata to write out default HTML metadata values for a page – when publishing a given SG, this calculation can be done a single time from that given point in the SG hierarchy)
I find that usually I want to push the context variables I am working with into the package so they can be easily accessed in both Dreamweaver Layout TBBs and .NET TBBs. To help work with Context Variables as package items I created a couple of helper functions that you can use in your template initialization logic as follows:
//Push global package items (from cache if available) if (!PushFromContextVariable("MyUsefulData")) { Dictionary<string, Item> items = new Dictionary<string, Item>(); //add code to read your configuration or other data here and add //separate package items to the items Dictionary PushAndAddToContextVariables(items, "MyUsefulData"); }
The source for the 2 helper functions is given below (these functions assume that you have _package and _engine member variables in your ITemplate class, which are initialized with the engine and package parameters passed to the Transform method)
protected bool PushFromContextVariable(string variableName) { if (_engine.PublishingContext.RenderContext != null && _engine.PublishingContext.RenderContext.ContextVariables.Contains(variableName)) { Dictionary<string, Item> items = _engine.PublishingContext.RenderContext.ContextVariables[variableName] as Dictionary<string, Item>; if (items != null) { foreach (string key in items.Keys) { _package.PushItem(key, items[key]); } } return true; } return false; } protected void PushAndAddToContextVariables(Dictionary<string, Item> items, string variableName) { if (items != null) { foreach (string key in items.Keys) { _package.PushItem(key, items[key]); } } if (_engine.PublishingContext.RenderContext != null && !_engine.PublishingContext.RenderContext.ContextVariables.Contains(variableName)) { _engine.PublishingContext.RenderContext.ContextVariables.Add(variableName, items); } }
While caching can be pretty useful, I have also used Context Variables in practice in the following different applications.Â
- Passing information between Page and Component Template – while it kind of breaks content modularity, it is sometimes it is unavoidable (and indeed forced by poor HTML design) to pass information between page and component templates. For example, I may want my CT to render slightly different HTML if it is rendering the first or the last Component Presentation on the page. By creating a Custom Function I can set the CP index as a context variable (from within the loop rendering the CPs in my Page Template), and then read that in my CT.
- Managing state within execution of a Dreamweaver Template. Conditionals and loops are possible using the Dreamweaver templating language, however sometimes it is useful to manage state within loops (especially nested loops). Nickoli’s original post has Custom Functions to set and get Context variables.
If you have found other uses for Context Variables it would be great to hear about them…
I’ve used the Tridion cache mechanism before (session.Cache) which has the same scope as the Context Variables, and never thought of using for passing information between CT and PT…
I guess that – functionally – there’s no difference between using the Cache and the ContextVariables – except that probably the Tridion Templating Cache is not meant to be used by our templates.
Hey Will,
I noticed the latest TemplateBase class in the Generic Usefull TBBs package on http://www.sdltridionworld.com has some of these methods. Thanks for explaining their usage in detail here.
I was wondering, can you share some approximate metrics you’ve experienced with using CVs for caching, e.g. saved 5,10 seconds in some scenarios?
No metrics to share, and as mentioned, I would not expect much performance increase in putting Tridion objects (config components etc.) into the context variables, as Tridion publishing caches these anyway. I think the real benefit is stored calculated items, like a tree of Folders/Structure Groups with metadata (note that you would not be able to put this type of object in the package though).
Hey Will,
Nice post. I was wondering cache functionality over transactions. So, I have configuration component which need to be readed during each publish. Can I cache this component somehow for certain time period?
Hi Vitaliy. If you need to cache an item over different publish transactions, then the method mentioned in this post will not apply, as the cache scope is a single transaction. You could try using the standard .NET cache; System.Runtime.Caching.MemoryCache – I never tried this, but I cannot think of a reason why your publisher service would not be able to use this. That said, I wouldnt put too much effort into caching of a single config component – Tridion has its own object caching which will work quite well, and reading a single component is not likely to be the bottleneck in your templates.