Mapping Umbraco content to POCOs for strongly typed views

Heads Up!

This article is several years old now, and much has happened since then, so please keep that in mind while reading it.

This article assumes you are familiar with using MVC views for Umbraco templates.

I'm not in the Umbraco code first camp. I get bored of people rattling on about uSiteBuilder, I just don't need it. At Moriyama we favour uSync for versioning and moving document types. I actually like the back office document type designer for creating my document types - it gives me a sense of structure, volume of document types and allows me to move stuff around quite quickly in a (reasonably) nice UI.

But before all of you code first nutcases start issuing death threats I'll concede one thing. Views against POCOs are much more readable and having proper intellisense is really neat. Also, views over POCOs can be much more readable to non .NET developers.

So although I don't use uSiteBuilder, I do (on occasion) map IPublishedContent to my own POCOs.

Which of the following two ways of outputting a document type property do you think is neater?

@inherits UmbracoTemplatePage

@(Model.Content.GetPropertyValue<string>("SiteName").Trim())
@(Model.Content.As<Article>.SiteName.Trim())

Figure 1: Article is the name of an as yet undefined POCO

Of course you answered the latter!

So how to achieve the latter? Firstly I create my POCOs manually - I don't like code generation (but it is just personal preference OK?)

using System;
namespace MvcApplication1.Poco
{
    public class Article
    {
        public string Title { get; set; }
        public string SiteName { get; set; }
        public DateTime UpdateDate { get; set; }
    }
}

Figure 2: Article is an Example POCO

The POCO has the same properties as your Umbraco document type, either default or custom. In the example above SiteName is a custom document type property.

The next step is to turn IPublishedContent into this POCO which we do with the help of an extension method with the following signature:

using System;
using Umbraco.Core.Models;
namespace MvcApplication1
{
    public static class PocoHelper
    {
        public static T As<T>(this IPublishedContent content) {

            var poco = Activator.CreateInstance<T>();

        }
    }
}

Figure 3: This is a signature for a method to be completed later

I'll come back to the implementation later, this just returns and empty POCO for now, but once complete I can start doing things in my view like:

@inherits UmbracoTemplatePage
@using MvcApplication1
@using MvcApplication1.Poco

@Html.CachedPartial("PageHeader", Model.Content.As<Article>(), 10)

Figure 4: Pass a POCO to a partial in an Umbraco View/Template

That allows me to have partial views that are strongly typed to a POCO like this:

@model MvcApplication1.Poco.Article

<h2>@Model.SiteName was updated on @Model.UpdateDate</h2>

Figure 5: A strongly typed and cached partial view

For some people, doing all of this kind of stuff in the view is a bit mucky, and I agree. Using route hijacking one could do all of my querying and converting before we get to the view (but I have an hour to write this article not a day).

So lastly, the implementation of my extension method:

using System;
using System.Reflection;
using Umbraco.Core.Models;
using Umbraco.Web;

namespace MvcApplication1
{
    public static class PocoHelper
    {
        public static T As<T>(this IPublishedContent content) {

            // Create an empty instance of the POCO
            var poco = Activator.CreateInstance<T>();

            // Discover properties of the poco with reflection
            PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

            var pocoType = poco.GetType();

            foreach (PropertyInfo propertyInfo in properties)
            {
                var contentType = content.GetType();
                if (content.GetType().GetProperty(propertyInfo.Name) != null)
                {
                    // It is a default propery - get the value with refelection
                    var propertyValue = contentType.GetProperty(propertyInfo.Name).GetValue(content, null);
                    pocoType.GetProperty(propertyInfo.Name).SetValue(poco, propertyValue, null);
                }
                else
                {
                    // it is a doctype property - ask Umbraco for the value
                    var propertyValue = content.GetPropertyValue(propertyInfo.Name);
                    pocoType.GetProperty(propertyInfo.Name).SetValue(poco, propertyValue, null);
                }
            }

            return poco;
        }
    }
}

Figure 6: Implementation of IPublishedContent to your POCO

Yes it is quite techie, but it is write once and you never see it again. Our real implementation of this is a bit more complex than that above.

We use the MemoryCache to check whether an object is available before constructing it. Before I conclude it is also worth mentioning that you'd probably need to work with Property Editor Convertors for more complex document type properties such as pickers.

So anyway, I've completely failed to put a festive twist on this article, It would have been hard to do (I guess I could have called a POCO property Santa or something). Hopefully it fills you with festive cheer anyway and I hope you've enjoyed the read.

Thanks to the guys behind 24days.in for the opportunity to post here. I'll write something up on using this technique combined with route hijacking and the MemoryCache on my blog in the new year.

Darren Ferguson

Darren is on Twitter as