Skip to main content

Back-End Development

Customizing the model within Sitecore MVC

Custom Tiles@1x.jpg

sclogovertpan_jpg_300In Sitecore, the MVC framework provides a “default” model type that does some very necessary work.

To support the view, Sitecore’s default model type Sitecore.Mvc.Presentation.RenderingModel provides you access to

  • The “current” item – passed via a datasource or the context item if no datasource is used
  • The PageContext item (which may be different if a datasource is used)
  • A reference to the Rendering used by the model

In some cases, this is very sufficient – but there are occasions where a developer might want to add a property to the model to remove code blocks that would otherwise have to be placed within the view.

Sitecore does allow you to create a custom model class – as long as you implement Sitecore.Mvc.Presentation.IRenderingModel, and support the above 3 properties, you can create a model from scratch.

Rather than writing a custom model from scratch every time I needed one, I decided to use Generics to accomplish the task.

Step 1 – Writing a CustomItem class

If you are familiar with Sitecore, you have certainly seen the Sitecore.Data.Items.CustomItem class.  This abstract type is a good foundation to use to write a custom domain item or DTO based on a Sitecore Item type in your project.  Lots of Sitecore custom modules such as the CustomItemGenerator use this base type when creating custom item classes.

Writing a custom item is easy – simply support a constructor that takes a Sitecore.Data.Item as an argument, and you’re ready to go.  Here’s a sample CustomItem to support a Breadcrumb view:

1using System;
2using System.Collections.Generic;
3using Sitecore.Data;
4using Sitecore.Data.Items;
5using Sitecore.Diagnostics;
6using MyStuff.Web.Extensions;
7 
8namespace MyProject.Web.Models.Items.Navigation
9{
10 
11public class BreadcrumbItem : CustomItem
12{
13/// <summary>
14/// Constructor to build a NavigableItem from a Sitecore Item
15/// </summary>
16/// <param name="item"></param>
17 
18protected BreadcrumbItem(Item item) : base(item)
19{
20CrumbStack = new Stack<Item>();
21Item cur = item;
22 
23while (cur != null && !cur.IsDerived(new ID(TemplateIds.WebsiteFolder)))
24{
25CrumbStack.Push(cur);
26cur = cur.Parent;
27}
28}
29public Stack<Item> CrumbStack { get; private set; }
30}
31}

Step 2 – Creating a Model base type that handles generics

First, the interface:

1using Sitecore.Data.Items;
2using Sitecore.Mvc.Presentation;
3 
4namespace MyProject.Web.Models
5{
6public interface ICustomItemRenderingModel<out T> : IRenderingModel where T : CustomItem
7{
8T CustomItem { get; }
9Item Item { get; }
10Item PageItem { get; }
11Rendering Rendering { get; }
12}
13}

The important part of this is the <out T> modifier – in C# 4, this allows you to use covariance … in simple terms, this allows you to use a derived type instead of a specific type as the generic type.  In this case, T can be of any type that is derived from CustomItem.

Now the implementation

1using System;
2using System.Reflection;
3using Sitecore.Data.Items;
4using Sitecore.Mvc.Presentation;
5 
6namespace MyProject.Web.Models
7{
8public class CustomItemRenderingModel&amp;lt;T&amp;gt; : ICustomItemRenderingModel&amp;lt;T&amp;gt; where T : CustomItem
9{
10private Rendering _rendering;
11private T _customItem;
12private Item _pageItem;
13private Item _item;
14 
15public void Initialize(Rendering rendering)
16{
17_rendering = rendering;
18}
19 
20public T CustomItem
21{
22get {
23if (_customItem != null) return _customItem;
24// find the constructor and call it
25Type type = typeof(T);
26ConstructorInfo constructor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance,
27null,
28new[] { typeof(Item) },
29null);
30if (constructor == null) return null;
31_customItem = (T)constructor.Invoke(new object[] { _rendering.Item });
32return _customItem;
33}
34}
35 
36public Item PageItem
37{
38get { return _pageItem ?? (_pageItem = PageContext.Current.Item); }
39}
40 
41public Item Item
42{
43get { return _item ?? (_item = _rendering.Item); }
44}
45 
46public virtual Rendering Rendering
47{
48get { return _rendering; }
49}
50}
51}

Step 3 – Using the Generic Type

This really should be the easy part, but not so much.  To use your new CustomItem / Generic model, you simply add the model statement to the view

@model MyProject.Web.Models.CustomItemRenderingModel<MyProject.Web.Models.Navigation.BreadcrumbItem>

and then of course in Sitecore create a Model item in the Layouts folder that declares the type.  Note that you have to use the alternate syntax to declare the type in your model item:

MyProject.Web.Models.CustomItemRenderingModel`1[MyProject.Web.Models.Items.Navigation.BreadcrumbItem],MyAssembly.Web

Step 4 – Replacing the Model Locator within Sitecore to support Generics

As of Sitecore 6.6 update 5, this step is required because Sitecore will fail to create the Model with the Generics declaration.  Once you have narrowed it down a bit, you have to do 2 things to support models with generics:

First, create your own model locator and override the method object GetModelFromTypeName(string typeName, string model, bool throwOnTypeCreationError)

1using System;
2using System.Text.RegularExpressions;
3using Sitecore.Mvc.Helpers;
4using Sitecore.Mvc.Presentation;
5 
6namespace MyProject.Web.Models
7{
8public class CustomModelLocator : ModelLocator
9{
10private const string GenericsPattern = @"`d[.+]";
11protected override object GetModelFromTypeName(string typeName, string model, bool throwOnTypeCreationError)
12{
13Type type = Regex.IsMatch(typeName, GenericsPattern, RegexOptions.Singleline) ? Type.GetType(typeName) : TypeHelper.GetType(typeName);
14 
15if (type == null)
16{
17if (!throwOnTypeCreationError) return null;
18throw new InvalidOperationException(Sitecore.StringExtensions.StringExtensions.FormatWith("Could not locate type '{0}'. Model reference: '{1}'", (object)typeName, (object)model));
19}
20object @object = TypeHelper.CreateObject(type, new object[0]);
21if (@object != null || !throwOnTypeCreationError)
22return @object;
23throw new InvalidOperationException(Sitecore.StringExtensions.StringExtensions.FormatWith("Could not create a model object of type '{0}'. Model reference: '{1}'", (object)typeName, (object)model));
24}
25}
26}

You can tell Sitecore to use your model locator by adding this method to the <initialize> pipeline

1using MyProject.Web.Models;
2using Sitecore;
3using Sitecore.Mvc.Configuration;
4using Sitecore.Pipelines;
5 
6namespace MyProject.Web.Pipelines
7{
8[UsedImplicitly]
9public class SetModelLocator
10{
11public virtual void Process(PipelineArgs args)
12{
13MvcSettings.RegisterObject&amp;lt;Sitecore.Mvc.Presentation.ModelLocator&amp;gt;(() =&amp;gt; new CustomModelLocator());
14}
15}
16}

Just patch that method into the pipeline (I did it as the first processor in the pipeline) and now you’re all set.

Thoughts on “Customizing the model within Sitecore MVC”

  1. I followed all the steps but still sitecore is not able to locate the generic model.

    Please explain Step3 in detail

    MyProject.Web.Models.CustomItemRenderingModel`1[MyProject.Web.Models.Items.Navigation.BreadcrumbItem],MyAssembly.Web

    Tell me the parameters I have to mention for step 3.

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