In theory, the magic of Dynamic Placeholders lets us have a container component placed onto your page more than once. That didn't work in the old world of "static" placeholders, because the rendering engine didn't like two placeholders with the same name. But despite it's benefits, the dynamic implementation has an annoying edge case – you may not be able to enable caching for your container component. I had a client bump into this issue recently, so I spent some time considering approaches that might help them address this issue.
@using Sitecore.Mvc <div style="border:2px solid red; margin-top:10px; padding:10px;"> <div>Dynamic Container</div> @Html.Sitecore().DynamicPlaceholder("container") </div>
If you build up a page using two instances of that component, with some child components inside each of the placeholders it will look fine in Experience Editor:
But if the container components have caching enabled, it won't look right on the public site:
That's because the code renders the first container and caches the result. Then it starts to render the second one, but gets a cache hit, and so returns the cached data for the first one. In the cache you see:
There's no cache variant that works for "this component varies by its children" – so only the first instance of the container ever gets rendered and cached.
My interest in this came about because I got handed a bug report on a client site that said "our page is wrong!" and it turned out to be because of this issue. I think there are three main ways that the issue could be resolved to make the client happy:
That's probably fine if your container has few children, or it's not used very much. The performance difference in that scenario is likely to be small – especially if you are able to enable caching for the child components inside your container.
That's a fix developers can roll out easily (it's just undoing the cache settings on the rendering item) and it doesn't affect editors at all.
No deployment is required for this, which is a bonus. But it might be confusing for editors to need to add a data source where it's not really needed. Plus this doesn't work if your container does need a datasource, and you have two on a page that need the same datasource but different children.
So this might not be a great answer.
To do this, you need to create a new data template to store your "cache by rendering's unique id" flag:
And then add that to the inheritance tree of the standard caching flags item:
And that lets you set a caching flag on your container rendering:
To act on that new flag, you need to deploy a simple bit of code to extend the rendering pipeline:
public class VaryByUniqueIdCacheKey : Sitecore.Mvc.Pipelines.Response.RenderRendering.GenerateCacheKey { protected override string GenerateKey(Rendering rendering, RenderRenderingArgs args) { var cacheKey = base.GenerateKey(rendering, args); var cacheField = (CheckboxField)rendering.RenderingItem.InnerItem.Fields["VaryByUniqueId"]; if (cacheField.Checked) { cacheKey += "_#uniqueId:" + rendering.UniqueId.ToString(); } return cacheKey; } }
That looks for the extra cache variant flag on the item being rendered, and if it exists the cache key is extended with the unique ID for this rendering instance.
And you can patch that into your config via:
<?xml version="1.0" encoding="utf-8" ?> <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <mvc.renderRendering> <processor patch:instead="processor[@type='Sitecore.Mvc.Pipelines.Response.RenderRendering.GenerateCacheKey, Sitecore.Mvc']" type="Caching.VaryByUniqueIdCacheKey, Caching" /> </mvc.renderRendering> </pipelines> </sitecore> </configuration>
Once that's done, you'll get a separate cache entry for each instance of your container component:
And the published version of the site will look correct:
Whether that solution is "better" for you than just disabling caching probably depends on the effort involved in rendering each of your containers. If they have one or two items in them, chances are it's not worth it. But if your container had a larger number of items, the "memory usage vs processing time" trade-off might be worth it.
It's an option for you...
↑ Back to top