Strongly typed vs. Dynamic Content Access
Heads Up!
This article is several years old now, and much has happened since then, so please keep that in mind while reading it.
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 :
- No intellisense when using Visual Studio or WebMatrix
- No compile errors. Because it's dynamic you won't see syntax errors until runtime.
- A small performance hit. Because Umbraco will try to map the Title property on the dynamic object to a property in the "real" content object. If you are interested you can see all the magic happening here : https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/src/Umbraco.Web/Models/DynamicPublishedContent.cs
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>
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>
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>
}
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>
}
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"); }
}
}
}
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>
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
- Understanding the dynamic keyword:
https://visualstudiomagazine.com/Articles/2011/02/01/Understanding-the-Dynamic-Keyword-in-C4.aspx - A four part article from Stéphane Gay on strongly typed models in Umbraco
http://www.zpqrtbnk.net/posts/strongly-typed-models-1
http://www.zpqrtbnk.net/posts/strongly-typed-models-2
http://www.zpqrtbnk.net/posts/strongly-typed-models-3
http://www.zpqrtbnk.net/posts/strongly-typed-models-4
Dave Woestenborghs
Dave is on Twitter as @dawoe21