Comparing ModelsBuilder and Ditto

Books I read
See all recommendations

If you've been reading my articles about typed models in Umbraco, you'll know I'm fairly biased towards ModelsBuilder. However, I never actually tried Ditto. I'll admit I didn't know exactly what I was telling you not to do in "stop mapping, start adapting". But I decided it was time to get dirty and do a comparison between the two. I'm still biased, but I'll do my best to stay objective.

This comparison is based on Matt Brailsford's Ditto demo. He's "Dittoified" the TXT starter kit in Umbraco, and I've tried my best to "ModelsBuilderify" the same.

There's only so much I could do in between my other projects, so it's fairly naïve. It misses some of the strong parts of what you can do with each approach in Umbraco. However, I hope I've been able to outline the main differences and their respective pros and cons.

All of the code for this post is available on github.

The project setup

The demo is built as a "website" in Visual Studio. This means we don't build, the binary files are included in source control, code lives in App_Code and whatnot. I'm not comfortable with that setup, but I'll live with it for the purpose of this post.

Handwritten code or not

I could argue that you'd hand write everything for Ditto and get everything for free with ModelsBuilder. It has a big fallacy though. To get the most out of ModelsBuilder, you'll write a lot of property value types, converters and not least interfaces. However, you'll get a slight increase in efficiency by using the "extract interface" refactoring of any refactoring tool for the latter. Property value types and converters goes for Ditto too. There's no big win or loss here.

Getting started

ModelsBuilder is bundled with Umbraco. Ditto can be installed from nuget. Both tools depend on property value converters in Umbraco. Which means they both also basically rely on Jeavon Leopold's Umbraco Core Property Value Converters, which is also about to be bundled, but can be installed from nuget for now.

As far as I can gather, there's no configuration needed for Ditto.

For ModelsBuilder, you'll have to enable it in web.config. It's a matter of flipping false to true in an app setting. But to make it truly useful, I set it to AppData mode and point the models to a sensible location.

<add key="Umbraco.ModelsBuilder.Enable" value="true" />
<add key="Umbraco.ModelsBuilder.ModelsMode" value="AppData" />
<add key="Umbraco.ModelsBuilder.ModelsDirectory" value="~/App_Code/Models" />

If this wasn't a website project, I'd put the models in ~/Models. There's also options to put them in an entirely different assembly. I'd also set a better namespace for the models.

If you're getting content in a controller or such and wonder how you get it typed, don't worry. You just have to cast it. For some reason people fail to understand this. When ModelsBuilder is active, you can't not get typed models. The content is decorated (typed) before it leaves the content cache.

Views

Both approaches uses generic view types instead of UmbracoTemplatePage as the base class. For Ditto you'll use DittoView<TModel> or a class derived from it. For ModelsBuilder, it's UmbracoTemplatePage<TModel> or a derived class. The top of your views now require:

@inherits OneOrTheOther<TypedModel>

The Model property of our views will then look like this for each tool:

DittoModelsBuilder
Culture (CultureInfo)Culture (CultureInfo)
Content (IPublishedContent)Content (TModel)
View (TModel)

One important distinction is that the ModelsBuilder TModel implements IPublishedContent, while Ditto has the typed model in a separate POCO (plain old C# object). I guess it's a matter of taste whether you like one or the other.

I compared them using UmbracoTemplatePage<TModel> here for similarity's sake. However, with ModelsBuilder you can actually make it simpler by inheriting UmbracoViewPage<TModel>. This will make the model the actual TModel instead of a RenderModel<TModel>. For the rest of the comparison, that's what I'll do. (I don't really care about the Culture since it's already in this.Culture. No need to add it to a model and get an extra train cart when referencing properties.)

There's one obvious quirk with anything that goes into an UmbracoViewPage: It has to be IPublishedContent. By creating another Razor base class, that can easily be avoided.

The one less train cart is a slight win in my opinion.

You'll either go Model.View.Property or Model.Property.

UmbTextPage

Let's dive in and look at how the different document types in TXT looks with both. UmbTextPage is the bread and butter of the site. We'll look at the Master view and UmbHome type a bit later since there's more complex things going on there.

Ditto uses the slim hand-written model TextPageViewModel. ModelsBuilder uses the more verbose generated UmbTextPage. They look like this:

Ditto

public class TextPageViewModel
{
    [Title]
    public string Title { get; set; }
    public string Image { get; set; }
    public HtmlString BodyText { get; set; }
}

ModelsBuilder

//------------------------------------------------------------------------------
// <auto-generated>
//   This code was generated by a tool.
//
//    Umbraco.ModelsBuilder v3.0.4.0
//
//   Changes to this file will be lost if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[PublishedContentModel("umbTextPage")]
public partial class UmbTextPage : UmbMaster
{
    // omitted ctors

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

    ///<summary>
    /// Featured Page?: Is this a page that should be featured on the home page?
    ///</summary>
    [ImplementPropertyType("featuredPage")]
    public bool FeaturedPage
    {
        get { return this.GetPropertyValue<bool>("featuredPage"); }
    }

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

On first glance, the ModelsBuilder class has a lot of stuff in it compared to the Ditto class. Remember that the ModelsBuilder one is autogenerated. You'll never poke around within that file. Notice that it's partial, so you can add to it in your own clean file. One other nice feature in it is that it adds the description of each property as an XmlDoc comment. You'll get the description of each property as IntelliSense when you go Model.[ctrl+space].

You probably noticed that they differ in which properties they contain. The Ditto model leaves the FeaturedPage property out, and the ModelsBuilder one leaves out the Title.

The Ditto model is meant to be a clean view model for the UmbTextPage template, where FeaturePage is completely irrelevant. So you've only got what you need to display the details of a text page in that model.

The ModelsBuilder model is an exact typed replica of the document type. It inherits UmbMaster, so naturally it also inherits the Title property from it. We can get a clean model based on this class too, but we'll get to that later.

Now what about that [Title] attribute on the Title property in the Ditto model? Seems a bit verbose doesn't it? It actually has a really useful purpose, which we'll look at when we get to UmbMaster.

For now, let's do a quick comparison of the property references in the views. You'll notice I've called the ModelsBuilder Title DisplayTitle. We'll get to that soon. There's also the issue of a missing value converter for the Upload property type. I'm lazy, so it's an object, hence the extra clutter.

DittoModelsBuilder
Model.View.TitleModel.DisplayTitle
if (!Model.View.Image.IsNullOrWhiteSpace()) { <img src="@Model.View.Image"> } if (!(Model.Image ?? "").ToString().IsNullOrWhiteSpace()) { <img src="@Model.Image"> }
@Model.View.BodyTextModel.BodyText

Not much to complain about here. They both have their merits, although with a string for the Image property, I'd vote for one less train cart.

[Title] Title or DisplayTitle

Let's dive a bit deeper into the common parts of all pages. We'll start with the title. It's quirky with both due to the fact that we'd like it to fall back to the name of a document if title isn't filled out.

Ditto

Ditto uses something called "processors" which is hooked up using attributes. The attribute classes contains logic for how a value should be transformed when mapped to a POCO. The title attribute looks like this:

public class TitleAttribute : DittoMultiProcessorAttribute
{
    public string TitleAttr { get; set; }

    public TitleAttribute()
        : base(Enumerable.Empty<DittoProcessorAttribute>())
    {
        base.Attributes.AddRange(new[] {
            new UmbracoPropertyAttribute(TitleAttr),
            new AltUmbracoPropertyAttribute("Name")
        });
    }
}

It re-uses two more attributes that specify which properties to use in order. I find this class a bit hard to get at first glance, but I'm sure I'd get it immediately if I used Ditto daily. A cool thing about it is that it's re-usable across POCOs and will always set the property to Title, or Name if Title is empty. It's also there in the demo to show how one can add a chain of fallbacks. The same functionality could have been added to the DTO with this slightly less contextful attribute instead:

[UmbracoProperty("title", "name")]

ModelsBuilder

With ModelsBuilder I added a new property called DisplayTitle to UmbMaster, and thereby all document types inheriting it:

public string DisplayTitle
{
    get { return Title.IfNullOrWhiteSpace(Name); }
}

Thus far, I dub the latter a clear winner in obviousness, but maybe not in naming. That can be solved, but in the interest of time and length of this post, I'll leave it for now. They're also equally re-usable as such.

The master: umbLayout

Now that we've seen a few differences, let's have a look at the bigger picture. We'll start at the top of umbLayout. Ditto of course has an individual view model, LayoutViewModel for the layout. With ModelsBuilder, I've extracted an interface from UmbMaster and called it ISiteContent. There's another processor for Ditto's model, and there's some more stuff in the ModelsBuilder partial:

Ditto

// LayoutViewModel.cs
[UmbracoProperties(Recursive = true)]
public class LayoutViewModel
{
    public string SiteName { get; set; }
    public string Byline { get; set; }
    public string Copyright { get; set; }
    [HomeLink]
    public Link HomeLink { get; set; }
}

// HomeLinkAttribute.cs
public class HomeLinkAttribute : DittoProcessorAttribute
{
    public override object ProcessValue()
    {
        var content = Value as IPublishedContent;
        if (content == null) return null;

        return content.AncestorOrSelf(1);
    }
}

// Link.cs
public class Link
{
    [UmbracoProperty("Name", Order = 0)]
    public string Title { get; set; }
    public string Url { get; set; }
    [UrlTarget]
    public string UrlTarget { get; set; }
}

ModelsBuilder

// ISiteContent.cs
public interface ISiteContent : IPublishedContent
{
    UmbHomePage Home { get; }
    string DisplayTitle { get; }
}

// UmbMaster.cs
public partial class UmbMaster : ISiteContent, ...
{
    private UmbHomePage home = null;
    public UmbHomePage Home
    {
        get
        {
            if (home == null)
                home = this.AncestorOrSelf<UmbHomePage>();
            return home;
        }
    }

The LayoutViewModel uses a built-in attribute in Ditto that says it should recurse up the tree for all the properties on the view model. It also has a hand-written processor that locates the Home node and maps it to a Link POCO. The Link POCO goes on and does some more processing and mapping. I'll not follow it further, but it's pretty magic and cool. Whenever whatever document is shown, Ditto can create a layout model for it based on its Home ancestor.

With UmbMaster I've cached up the Home ancestor whenever you access it. It'll be available as a train cart on all pages. ISiteContent acts as a slimmer view model if you want, for all types. Hence it can be used in umbLayout. The difference from Ditto is that it also IS your actual content.

Let's look at how they're used in the view.

Use caseDittoModelsBuilder
Title tagModel.CurrentPage.Name | Model.View.SiteNameModel.DisplayTitle | Model.Home.SiteName
Home URLModel.View.HomeLink.UrlModel.Home.Url
LogoModel.View.SiteNameModel.Home.SiteName
BylineModel.View.BylineModel.Home.Byline
CopyrightModel.View.CopyrightModel.Home.Copyright

It's a pretty darn close race. I think Matt just forgot to add the [Title]Title to the view model, 'cause he's missing out on the fallback there. Might be I'm too eager and the original TXT just showed the name though. In any case, it's just a matter of one more or one less train cart again.

I'm slightly biased towards the reference to Home. It makes it more obvious where the value comes from. We also just traverse the hierarchy once, instead of for all properties.

But we're also starting to see a growth in code amount on the Ditto side. There's more processors, more POCOs and more logic to follow if we want to figure out what happens. That's a slight win for ModelsBuilder in my opinion. Weight should be given the magic coolness of Ditto's processors though.

The partials

There's a lot more going on in umbLayout. For starters, there's the navigation. You can probably guess by now that Ditto's got a TopNavigationViewModel. On the ModelsBuilder side, we've got an INavigation interface. I've added INavigation to UmbMaster so all documents implements navigation. We could've stuck it on home only, but there's something to demonstrate here. We'll see the other option next.

Let's look at the implementations first:

Ditto

// TopNavigationViewModel.cs
public class TopNavigationViewModel
{
    [MainNav]
    public IEnumerable<NavLink> MenuItems { get; set; }
}

// MainNavAttribute.cs
public class MainNavAttribute : DittoProcessorAttribute
{
    public override object ProcessValue()
    {
        var content = Value as IPublishedContent;
        if (content == null) return Enumerable.Empty<NavLink>();

        var homePage = content.AncestorsOrSelf(1).First();
        return new[] { homePage }
            .Union(
                homePage.Children
                .Where(x => x.IsVisible())
            );
    }
}

ModelsBuilder

// INavigation.cs
public interface INavigation : IPublishedContent
{
    IEnumerable<ISiteContent> MenuItems { get; }
}

// UmbMaster.cs
public partial class UmbMaster : ISiteContent, INavigation
{
    // Home & DisplayTitle omitted...

    IEnumerable<ISiteContent> INavigation.MenuItems
    {
        get { return home.MenuItems; }
    }
}

// UmbHome.cs
public class UmbHome : INavigation, ...
{
    // Quite a lot omitted...

    public IEnumerable<ISiteContent> MenuItems
    {
        get
        {
            return new[] {this}
                .Union(
                    Children
                    .OfType<ISiteContent>()
                    .Where(c => c.IsVisible())
                );
        }
    } 

As you can see, Ditto keeps processing and separating concerns. That's a really good feature with Ditto - it really makes you adhere to the Single Responsibility Principle. I like that as much as I like the Interface Segregation Principle.

On the ModelsBuilder side, I've done a little dispatch from UmbMaster to UmbHome. Since UmbHome derives from UmbMaster, it'll go through both properties, but end up the same. All others will get the menu items from Home.

The views are completely equal, except for the extra train cart with Ditto.

Further, we've got the featured and about parts near the bottom of the page. I've only added those to UmbHome with ModelsBuilder. This means that we can't just call the partials from layout any more. We need to pass in Model.Home. That's more or less the only difference. The implementation follows the same patterns as before. There's individual POCOs with Ditto, and segregated interfaces with ModelsBuilder. No big wins or losses there. I'll skip the code, you can see it on GitHub.

One thing to notice if you didn't already, is that when we create segregated interfaces as view models with other property names, we implement them explicitly. By doing that, we avoid cluttering up the main model with more properties. We can also use the same name for different actual properties. Here's the IAbout implementation on UmbHomePage:

string IAbout.Title { get { return AboutTitle; } }
IHtmlString IAbout.Text { get { return AboutText; } }

See how it can be named Title without messing with the actual Title from UmbMaster?

It's passed from layout to the view like so:

@Html.RenderPartial("umbAbout", Model.Home);

Functionality and behavior

There's a lot of cool things you can do with Ditto processors. Matt's added paging to the news overview. I didn't get around to implement any paging for the news using a ModelsBuilder approach. That's mainly because there aren't any ModelsBuilder approach for it. I'd implement it using a controller for the paging and possibly a html helper for the pager. I might even be so lazy that I'd do it in the view. With Ditto it's done with an attribute, and it's also cached using another attribute. Thing is, it amounts to some 50 lines plus to make it nicely separated and modelified. I'm willing to bet it could be done with a lot less code using traditional techniques. We can argue for a while which method has the best separation of concerns.

As far as I could find, there's only the paging that is extra functionality in the Dittoified version.

What about Nested Content?

Of course Nested Content works OOTB with Ditto. Everything does. With ModelsBuilder, you'd have to decorate the IPublishedContent yourself since the current property value converters doesn't. I can't imagine that's far off into the future though. If we had a nested content type of some type implementing banners, we could go as such with ModelsBuilder:

var banners = Model.Banners.Select(b => new Banner(b));

Where Banner is generated due to the doctype. A converter could go through the factory like so:

PublishedContentModelFactoryResolver.Current.Factory.CreateModel(content);

Architecture and re-use

Both the POCOs and the interfaces can be put in separate assemblies and re-used across solutions. The interfaces can be pretty dependencyless, while the Ditto POCOs will depend on Ditto. By changing the implementation from properties on the partials to extensions for the interfaces, you can also re-use most of the logic with the ModelsBuilder approach. This will make them adhere better to the single responsibility principle. All in all, there's no obvious big winner here either. It's up to the implementor to keep it clear and simple.

Ditto isn't limited to Umbraco types. It can be used for anything that can be reflected. This means you can use it for your third-party e-commerce package for instance.

Using well structured and SOLID object oriented programming you can achieve the same without depending on any tool.

Amount of code

Counting all classes in the Dittoified version, I get to 18 hand crafted view models. There's 14 processors and a news context of some sort. There's also 6 more classes including controllers and extensions. Totaling 38 different classes to know and build. I didn't count the lines, but there's several hundred.

On the ModelsBuilder side, I got 5 classes generated for free. I extracted 7 interfaces and modified them a little bit. I wrote 4 partial implementations for the generated models. Mostly the same queries as in the 14 processors for Ditto. Totaling ~10 semi hand crafted classes of ~200 lines.

Conclusions

I've only identified a few small wins for ModelsBuilder, and I'm sure that's because I'm biased. Ultimately it boils down to preference. My biggest issues are dependencies, attributes, learning curve and amount of code. There's also a risk of scattering logic too much, making it difficult to find out what's going on. Putting everything in processors seems like an unnecessary shift away from traditional MVC and OOP. Still, Ditto radiates an intriguing magic aura, and I look forward to following its progress.

I've only been able to scratch the surface of both tools and approaches without having to write an entire book. So I encourage you to compare the two projects yourself and see which fits your needs and style the best.

I think I managed to be quite objective, so I'd like to finish off with a really subjective opinion. Using attributes which depend on a library clutters up your dependency chain. They might give meaning in context, and might live within a bounded context where it's alright to have the dependency. They may also help you stay DRY and separate concerns. ModelsBuilder also adds attributes to its classes, so there's that. Generally though, I try to steer away from them. Agree or not, all of the non-generated ModelsBuilder code is dependencyless and impossible not to get at first glance (OK, they depend on Umbraco.Core). Incidentally, my discontinued competitor to ModelsBuilder, Umbraco CodeGen, generates 100% attribute- and dependencyless models (OK, they depend on Umbraco.Core). I'm regularly pestering Stephane Gay about opening up the ModelsBuilder writer so I can swap out the generated classes with clean, dependencyless beauty. When that happens, you'll be in full control over your generated models.

Happy modelling, and stay DRY!

Author

comments powered by Disqus