Strongly typed vs. Dynamic Content Access

I frequently see posts on our.umbraco.org where people post questions on how to retrieve content from Umbraco. Often they have problems because they are mixing strongly typed content access with dynamics. Probably because they have seen some different examples on the web and trying to mix stuff from these examples.

In this article I want to explain the differences, the pros and the cons of each approach.

Templating

When creating MVC templates in Umbraco you have 2 types you can inherit from.

@inherits UmbracoTemplatePage

And

@inherits UmbracoViewPage

Both of these provide access to the UmbracoHelper, so you can do things like @Umbraco.Field("fieldname") or @Umbraco.GetDictionaryValue("dictionaryKey")

UmbracoTemplatePage

When you use this, your model will be of the type Umbraco.Web.Models.RenderModel.

The model will have a property called Content that is a implementation of the interface Umbraco.Core.Models.IPublishedContent

This template type will also expose a property called CurrentPage of the type Umbraco.Web.Models.DynamicPublishedContent

UmbracoViewPage

When you inherit from this, your model will always be an implementation of the type Umbraco.Core.Models.IPublishedContent

UmbracoViewPage<T> and UmbracoTemplatePage<T>

With these generic versions you can also pass in your own model:

@inherits UmbracoViewPage<NewsItem>

So if you have a closer look at the Umbraco Source code you will see that UmbracoViewPage inherits from UmbracoViewPage<IPublishedContent> and that UmbracoTemplatePage inherits from UmbracoViewPage<RenderModel>

Content access

So now we have seen what the different options are for setting up your template let's have a look what the difference is between IPublishedContent and its dynamic counterpart

IPublishedContent vs DynamicPublishedContent

When using strongly typed content access, the Umbraco frontend cache will return an object implementing the IPublishedContent interface. In a default Umbraco install this will be an object of the type Umbraco.Web.PublishedCache.XmlPublishedCache.XmlPublishedContent

The object will expose properties and methods needed for retrieving the content of the current item. But also methods for retrieving the children, descendants, parent, ancestors, ...

When we use the dynamic version we get an object of the type Umbraco.Web.Models.DynamicPublishedContent (which actually implements the interface IPublishedContent)

So let's see how it works in practice.

We have the following 2 document types set up in our plain Umbraco install.

  • Homepage
  • News item
    • Title : textbox
    • News Date : date picker
    • Overview text : textarea
    • Image : Media picker
    • Text : Rich Text Editor
    • Show on homepage : true/false

Using the strongly typed syntax we can use this to show the title

@Model.GetPropertValue<string>("title")

Using DynamicPublishedContent object we would do this:

@CurrentPage.Title

So when we look at this the dynamic variant looks much more human readable and feels more natural when you are used to object oriented programming. But there are also some downsides :

It's worth mentioning that the strongly typed version is also not failsafe. If you make a typo in your property name it will not render. And this is a mistake that is sometimes easy to look over when debugging.

So for our complete news item template it would look like this for the strongly typed version:

@using Umbraco.Web 
@using Umbraco.Web.Mvc
@inherits UmbracoViewPage
@{
    Layout = null;
}

<div>
    <h1>@(Model.HasValue("title") ? Model.GetPropertyValue<string>("title") : Model.Name)</h1>

    <p>@(Model.HasValue("newsDate") ? Model.GetPropertyValue<DateTime>("newsDate").ToString("dd MMMM yyyy") : Model.CreateDate.ToString("dd MMMM yyyy"))</p>

    @if(Model.HasValue("image"))
    {
        var mediaId = Model.GetPropertyValue<int>("image");
        var mediaItem = Umbraco.TypedMedia(mediaId);
        if(mediaItem != null && mediaItem.DocumentTypeAlias == "Image")
        {
            <p><img src="@mediaItem.GetCropUrl(200,200)" alt="" /></p>    
        }
    }

    <div>@(Model.GetPropertyValue<IHtmlString>("text"))</div>
</div>

News detail template using the IPublished content object

For the dynamic version it will look like this:

@using Umbraco.Web 
@using Umbraco.Web.Mvc
@inherits UmbracoTemplatePage
@{
    Layout = null;
}

<div>
    <h1>@(CurrentPage.Title != null ? CurrentPage.Title : CurrentPage.Name)</h1>

    <p>@(CurrentPage.NewsDate != DateTime.MinValue ? CurrentPage.NewsDate.ToString("dd MMMM yyyy") : CurrentPage.CreateDate.ToString("dd MMMM yyyy"))</p>

    @if(CurrentPage.Image != null)
    {
        var mediaId = CurrentPage.Image;
        var mediaItem = Umbraco.Media(mediaId);
        <text>@mediaItem.Name</text>
        if(mediaItem != null)
        {           
            <p><img src="@mediaItem.Url" alt="" /></p>    
        }
    }

    <div>@CurrentPage.Text</div>
</div>

News detail template using the DynamicPublishedContent object

At this point the dynamics variant looks easier to write and read. The performance penalty for using dynamics would also be negligible.

But what if we want to render a list of items with some filtering applied and ordering. Let's say we want to render a list of news items on the homepage that are marked to display on the homepage (property Show on homepage) and order them by newsdate descending, or createdate when newsdate isn't filled.

With the strongly typed version we would get our newsitems for the homepage like this:

Model.Children.Where(x => x.GetPropertyValue<bool>("showOnHomepage"))
.OrderByDescending(x => x.HasValue("newsDate") ?
x.GetPropertyValue<DateTime>("newsDate") : x.CreateDate)

With the dynamic version this would look like this:

CurrentPage.newsItems.Where("showOnHomepage")
.OrderBy("createDate desc").ToList();

We see immediately it is not possible to order on newsdate and use createdate when this is not filled (or maybe it's just me not knowing how to do it). Also the where statement is relatively easy here. But this can become pretty complex when you want to filter on multiple properties. Because you have to write everything as string, instead of a lambda expression, like you do with the strongly typed version.

This is an example taken from the Umbraco documentation:
CurrentPage.Where("NodeTypeAlias == @0 && Level == @1 || Name = @2", "HomePage", 0, "Home");

Here is the entire code example for rendering the news list on the homepage using strongly typed content access:

@using Umbraco.Web
@using Umbraco.Web.Mvc
@inherits UmbracoViewPage
@{
    Layout = null;
    var newsItems = Model.Children.Where(x => x.GetPropertyValue<bool>("showOnHomepage")).OrderByDescending(x => x.HasValue("newsDate") ? x.GetPropertyValue<DateTime>("newsDate") : x.CreateDate).ToList();
}
@foreach(var item in newsItems)
{
    <div>
        <h2>@(item.HasValue("title") ? item.GetPropertyValue<string>("title") : item.Name)</h2>
        <p>@(item.HasValue("newsDate") ? item.GetPropertyValue<DateTime>("newsDate").ToString("dd MMMM yyyy") : item.CreateDate.ToString("dd MMMM yyyy"))</p>
        @if(item.HasValue("overviewText")){
            <p>@(item.GetPropertyValue<string>("overviewText"))</p>
        }
        <p><a href="@item.Url">Read more</a></p>
    </div>
}

Code snippet for news list on homepage using the IPublishedContent object

And here is the same snippet using dynamics :

@using Umbraco.Web
@using Umbraco.Web.Mvc
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
    Layout = null;
    var newsItems = CurrentPage.Children.Where("showOnHomepage").OrderBy("createDate desc").ToList();
}
@foreach(var item in newsItems)
{
    <div>
        <h2>@(item.Title != null ? item.Title : item.Name)</h2>
        <p>@(item.NewsDate != DateTime.MinValue ? item.NewsDate.ToString("dd MMMM yyyy") : item.CreateDate.ToString("dd MMMM yyyy"))</p>
        @if(item.OverviewText != null){
            <p>@item.OverviewText</p>
        }
        <p><a href="@item.Url">Read more</a></p>
    </div>
}

Code snippet for news list on homepage using the DynamicPublishedContent object

The performance penalty will be much higher when using dynamics and applying filters on a list because "where" string need to be parsed. In this simple example the dynamics version was on average 40% slower.

So let's summarize what we have discussed until now:

  • Dynamics are easier to write and read for simple things
  • Filtering and ordering is more complex using dynamics
  • Dynamics don't offer intellisense
  • With both versions typos in property names won't throw an error, but are difficult to pinpoint.
  • Dynamics can cause a performance hit.

Alternatives

Luckily there are some alternatives around that will let you work with strongly typed models. The one I want to discuss with you today is https://our.umbraco.org/projects/developer-tools/zbumodelsbuilder/

This is created by HQ employee Stéphane Gay and will be integrated in Umbraco 7.4

This generates strongly typed models for your document types. So in our case we would get a model looking like this, representing our news item.

//------------------------------------------------------------------------------
// <auto-generated>
//   This code was generated by a tool.
//
//    Zbu.ModelsBuilder v2.1.5.54
//
//   Changes to this file will be lost if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Web;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web;
using Zbu.ModelsBuilder;
using Zbu.ModelsBuilder.Umbraco;

namespace Umbraco.Web.PublishedContentModels
{
	/// <summary>News Item</summary>
	[PublishedContentModel("NewsItem")]
	public partial class NewsItem : PublishedContentModel
	{
#pragma warning disable 0109 // new is redundant
		public new const string ModelTypeAlias = "NewsItem";
		public new const PublishedItemType ModelItemType = PublishedItemType.Content;
#pragma warning restore 0109

		public NewsItem(IPublishedContent content)
			: base(content)
		{ }

#pragma warning disable 0109 // new is redundant
		public new static PublishedContentType GetModelContentType()
		{
			return PublishedContentType.Get(ModelItemType, ModelTypeAlias);
		}
#pragma warning restore 0109

		public static PublishedPropertyType GetModelPropertyType<TValue>(Expression<Func<NewsItem, TValue>> selector)
		{
			return PublishedContentModelUtility.GetModelPropertyType(GetModelContentType(), selector);
		}

		///<summary>
		///Image
		///</summary>
		[ImplementPropertyType("image")]
		public object Image
		{
			get { return this.GetPropertyValue("image"); }
		}

		///<summary>
		///News date
		///</summary>
		[ImplementPropertyType("newsDate")]
		public DateTime NewsDate
		{
			get { return this.GetPropertyValue<DateTime>("newsDate"); }
		}

		///<summary>
		///Overview text
		///</summary>
		[ImplementPropertyType("overviewText")]
		public object OverviewText
		{
			get { return this.GetPropertyValue("overviewText"); }
		}

		///<summary>
		///Show on homepage
		///</summary>
		[ImplementPropertyType("showOnHomepage")]
		public bool ShowOnHomepage
		{
			get { return this.GetPropertyValue<bool>("showOnHomepage"); }
		}

		///<summary>
		///Text
		///</summary>
		[ImplementPropertyType("text")]
		public IHtmlString Text
		{
			get { return this.GetPropertyValue<IHtmlString>("text"); }
		}

		///<summary>
		///Title
		///</summary>
		[ImplementPropertyType("title")]
		public string Title
		{
			get { return this.GetPropertyValue<string>("title"); }
		}
	}
}

C# class generated by Zbu Models builder for the newsitem doctype

 

Then in our views when we access our Model it will be of the type NewsItem.

@using Umbraco.Web
@using Umbraco.Web.Mvc
@using Umbraco.Web.PublishedContentModels
@inherits UmbracoViewPage<NewsItem>
@{
    Layout = null;
}
<div>
    <h1>@(!string.IsNullOrEmpty(Model.Title) ? Model.Title : Model.Name)</h1>

    <p>@(Model.NewsDate != DateTime.MinValue ? Model.NewsDate.ToString("dd MMMM yyyy") : Model.CreateDate.ToString("dd MMMM yyyy"))</p>

    @if(Model.Image != null)
    {
        var mediaId = Model.Image;
        var mediaItem = Umbraco.TypedMedia(mediaId);
        if(mediaItem != null && mediaItem.DocumentTypeAlias == "Image")
        {
            <p><img src="@mediaItem.Url" alt="" /></p>    
        }
    }

    <div>@Model.Text</div>
</div>

News detail template using the model created by the Zbu Models builder

The benefits of this approach are :

  • Much more readable and easier to write views
  • Compile errors on syntax errors
  • Intellisense in Visual Studio and Webmatrix

If you want to know more about the models builder watch this uHangout episode about it.

If you don't like your models to be generated and want to have more control on how your models are setup, I would recommend using Ditto. There is also a uHangout episode about using it.

Wrap up

Always try to use strongly typed content access. Using dynamics can look easier to write and read but that doesn't weigh up to the downsides. And with alternatives like Zbu Models builder it makes using strongly typed models even easier.

In my opinion dynamics have served their purpose. They were introduced in Umbraco with the first implementation of Razor, somewhere in a v4.x version. But the core should actually think about removing it in a future version (V8 ?).

Interesting reading

Dave Woestenborghs

Dave is on Twitter as