Sitecore MVC Rendering Datasources

07 March 2016

Problem

Today I got a report of a bug with a rendering on a product page of our site. I checked the usual suspects; are there any broken links in the item? is it published? is everything referenced by this page published? None of the above.

It turned out that someone had set a datasource on a container rendering causing child renderings to use that datasource instead of the page item. Setting a datasource on a rendering with no fields would not matter in regular Sitecore, however in Sitecore MVC this changes the rendering context for child renderings. In this case the renderings within the placeholders of the container rendering broke as they were now attempting to render data from a different type of item.

Solution

The root of the problem (rogue users aside) was that the controller used RenderingContext.Current.Rendering.Item assuming that the rendering (and the container rendering) would never have a datasource set and would thus get their data from the page item. This is the most flexible approach if you have a component which potentially could use the page item or a datasource item. The solution was to instead use RenderingContext.Current.PageContext.Item which always references the page item irrespective of the datasource value or that of any parent renderings.

How to get a Sitecore MVC rendering datasource

There are three ways in Sitecore MVC to access a datasource/context item for a rendering all available from Sitecore.Mvc.Presentation.RenderingContext.Current.

#1 RenderingContext.Current.Rendering.Item

The datasource of the current rendering, or the datasource of an ancestor rendering if specified, or the page item if no datasource set and no ancestor rendering with datasource. Rendering datasource takes priority.

  • Ideal for components e.g. sidebar items where the datasource is a separate item from the page.
  • Flexible. Allows rendering to fall back to context item (page or parent rendering) if datasource is empty.

#2 RenderingContext.Current.ContextItem

The the current page item, or the datasource of the ancestor rendering.

  • Ignores rendering datasource.
  • References the page item OR, if set, the datasource of an ancestor rendering.
  • Enables you to swap datasources of several renderings at once within a page.
  • Less commonly needed.

#3 RenderingContext.Current.PageContext.Item

The page item (equivalent to writing Sitecore.Context.Item in WebForms)

  • Ignores datasource set on rendering.
  • Ignores datasource of any ancestor renderings.

TL, DR

In short, for Sitecore MVC renderings which render content of the current page use:
RenderingContext.Current.PageContext.Item

For renderings which have a datasource set to a different item than the current page, use:
RenderingContext.Current.Rendering.Item

I have made a simple solution to demonstrate these three ways of passing context to a rendering: https://github.com/dresser/SitecoreMvcRenderingDataSources.
(Uses Sitecore 8.0 rev. 150812)

Definitions

Container Rendering: a rendering which does not display any content from Sitecore and serves only to render HTML markup and define placeholders for other renderings.

Edit: July 2016

Kamruz Jaman mentioned to me the Mvc.AllowDataSourceNesting setting which controls how RenderingContext.Current.ContextItem behaves. The setting can be found in App_Config\Include\Sitecore.Mvc.config.

Mvc.AllowDataSourceNesting="true" (default)

This is the default behaviour described in this article. When true, the ContextItem property can return the datasource item of the parent rendering if the datasource of the current rendering is not set.

Mvc.AllowDataSourceNesting="false"

When false, the ContextItem either renders the datasource of the current rendering (if set), or the context item of the page.

Further Reading

As usual, I was not the only one to have reported this behaviour. Here are some other related articles:

Tags: Sitecore APISitecore MVC
comments powered by Disqus