Skip to main content

Back-End Development

Sitecore MVC Item Maze

Maze@1x.jpg

The Maze

If you have worked with Sitecore MVC you have probably noticed a few (to say the least) .Item properties in various contexts:

  • Model.Item (where Model is a RenderingModel)
  • Model.PageItem
  • Model.Rendering.Item (or RenderingContext.Current.Rendering.Item)
  • Model.Rendering.RenderingItem
  • RenderingContext.Current.ContextItem
  • PageContext.Current.Item
  • @Html.Sitecore().CurrentItem

And of course a good old Context.Item is always available. A maze indeed.

Intrigued? Need a map? Ok, pack your lunch and let’s go look around. If you’re patient we will make it out by sunset.

Not the Item you need

Let’s start by marking the dead ends and wrong turns and by pointing out the two Items you most likely don’t need

1. Rendering.RenderingItem

Your rendering is represented by Sitecore.Mvc.Presentation.Rendering object. You can get to it via:

RenderingContext.Current.Rendering

or via:

@Model.Rendering

when in your razor view, provided that your model is an instance of the RenderingModel. The RenderingItem property points to the definition item of your component, the item under /sitecore/layout/Renderings. It’s probably not the item you need.

2. Context.Item

A good old friend… It’s hard, I know, but it’s time to move on. Depending on where you are in the Sitecore MVC land it may or may not be the item you need. It’s probably worth a blog post of its own but I would recommend you just don’t use Context.Item in your Sitecore MVC journey.

The two items you need

You most likely need one of the two items – the current page item, and the current component’s data source item. Or let’s better call it the current component’s context item as your rendering might not be datasource-driven, for example.

1. Current Page Item

In context of a rendering (view and controller rendering alike), a page item is the item that’s being rendered. The one that carries your rendering in its layout definition.

var page = PageContext.Current.Item;

And it is normally determined by running the mvc.getPageItem pipeline. We’re in a maze though, remember? It’s probably that item. It’s most likely that item. It can, however, be explicitly set on the current PageContext. And the latter could also be changed by pushing a new context object onto a stack using ContextService.Get().Push(). That said, if no code in your solution is trying to outsmart Sitecore you can be pretty sure it’s the current page item.

In context of a routed request it’s the request’s context item. You can read more about it here. It’s the same idea actually – mvc.getPageItem pipeline. The processors in it will take turns to try and figure out what should be the page item and the guys there know about route values and {*scItemPath}.

[su_note note_color=”#fafafa”]If you’re in context of a view rendering and your model is a (or a descendant of) RenderingModel (and why wouldn’t it be?) you can just use:

@Model.PageItem

to explicitly refer to the current page. We’ll see how you can, in some circumstances, get the same item implicitly via Model.Item but I am a big proponent of stating your intention as clear as possible when writing your code. I always favor SingleOrDefault() and Single() over FirstOrDefault() or First(), for example, unless, of course, your intent is, in fact, to pick the first out of potentially many.
[/su_note]

2. Current Context Item

In context of a routed request there’s only one context item and we just talked about it. This particular one only makes sense in context of a rendering.

Let’s start with an example. In your razor view:

@Html.Sitecore().Field("Title")

will render a Title field of what item exactly?

Is it the field of:

@Html.Sitecore().CurrentItem

or

@Model.Item

or

@Model.Rendering.Item

or

RenderingContext.Current.Rendering.Item

?

The answer is “Yes“.

The last three are all the same. The CurrentItem on the helper will almost always be equal to them as well. There’s one specific case when it won’t be and I will explain in a minute. The Item property of the Rendering object does the following:

public virtual Item Item
{
    get
    {
        return this.StaticItem ?? this.GetItemFromContext();
    }
    set
    {
        this.StaticItem = value;
    }
}

which roughly (please read the code for the exact transcript) translates to the following sequence:

  1. Try to get the item from the rendering’s ItemId property. Return it if not null. I never used and I am not really sure what it is to be honest
  2. Try to get the item from the rendering’s Data Source. Return it if not null
  3. Try to get the ContextItem from the current RenderingContext if datasource nesting is allowed. This thing deserves special attention and I will give it its due in the next chapter. Don’t return just yet if not null.
  4. If the ContextItem was null (or datasource nesting disabled) then grab the current page item from the current PageContext. Don’t return it just yet if not null.
  5. If nothing found then return null. Can’t see why you would have a rendering running outside of a page context (and thus without the current page item) but nothing is impossible with Sitecore
  6. If we have either the ContextItem or the page item and the rendering’s Data Source property is null or empty then return that item we have
  7. Otherwise try to evaluate the Data Source relative to that item we have

Still with me? Ok then, moving on.

Remember I told you the CurrentItem on the helper does it a little differently? It actually runs this exact sequence with one extra step at the very end. In case after all these attempts to get something back we got a null (and how could we? do you see why we would get a null after all the hassle we just went through to get something?), so in case we have nothing at the end it will one more time reach out and grab the page item. I guess the helper hold itself to a slightly higher standard.

[su_note note_color=”#fafafa”]It’s that last step in the long sequence that may produce a null. If, for example, your rendering’s data source was an ID that was no longer in your Sitecore database or maybe it was there but it was not in the right database, it would return null on the second step and would later try to get it again relative to the ContextItem or the page item. An ID is not a path so Sitecore would basically just do the GetItem() by that ID again. It’s silly, I know, but it’s what it does and that’s how you can end up with a null. I had it happen to me so that’s how I know[/su_note]

The Hidden Gem Item

So what was that ContextItem on the RenderingContext? Is it an implementation detail or can it come in handy?

The mvc.renderRendering pipeline runs EnterRenderingContext processor right before executing the Renderer. This processor is tasked with pushing the new RenderingContext for the rendering that is about to render onto the stack of the ContextService. In case a rendering is nested within another rendering the ContextItem will be set to the outer rendering’s Item – the result of running the sequence we have just looked into in the previous chapter – it’s called datasource nesting. Otherwise it will be set to be the current page item.

There’s a setting that controls whether datasource nesting is allowed – Mvc.AllowDataSourceNesting. It defaults to true but if you set it to false the ContextItem will not be used to resolve the current context item.

This hidden gem item can come in very handy. Next time (or maybe the time after next as I already have another blog post in the making) I will tell you how you can use it to your advantage. Stay tuned!

Thoughts on “Sitecore MVC Item Maze”

  1. Thanks for taking the time to go into this in detail – as you point out, the context item situation was getting a bit confusing.

  2. Cristian Delgado

    One question. Is there a way to set the context item somehow without using custom item resolvers?

  3. If you want to set it temporarily you can always use the

    using (new ContextItemSwitcher(myItem))
    {
    // Sitecre.Context.Item now points to myItem
    }

  4. Pingback: Resolving Context.Item in Sitecore MVC | jammykam

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Brian Beckham

As a Sitecore MVP, Brian spends most of his time consulting and architecting software solutions for enterprise-level Sitecore projects.

More from this Author

Follow Us