How To Write Safe Testable Code When Using Dynamic Model Binding With Umbraco MVC

After going through the code on a recent project, I came across a lot of boiler-plate code. The site was built in web forms and used dynamic model binding directly in code that looked like this:

var node= Node.GetCurrent();
node.GetProperty("contentTitle").Value;

Personally, I think there is a lot wrong with this line of code:

  • Not strongly-typed As I work with a number of different platforms, I will always highly recommend using strongly-typed model binding for a host of reasons. In Umbraco, you have several options, the strongest being GlassMapper or uSiteBuilder. If, like me, you find yourself working on a legacy system and you can’t easily switch from dynamic model binding to a strongly-typed system, then it’s not a good idea to write code that won’t work
  • Not Testable As the code is using a static helper, the class cannot be unit tested
  • Not Flexible For Upgrades This was the main reason I started to look at this code. After an upgrade from 6.1.2, to 7.5, the site started to blow up. Having code in this manner really tightly couples your code with your Umbraco instance. If Umbraco introduces a new better API, you have to go through your whole project to update it. On the other hand, if you wrap all your calls up into a string method that returns a string. If the API updates, you can change one method and the whole of your website will update. This benefit alone can save months of development and refactor down the line.
  • No Null Checks

I’m hoping my arguments have convinced you that wrapping up all calls to GetProperty() is beneficial to your code base. If you are still a little unsure about how to implement this type of change, I’ll walk you through it.

Writing Safe Code

The problem with the code in the example above, is that if node.GetProperty(“contentTitle”)is empty PropertyValue() will throw an object reference exception.

My personal preference when dealing with content is a fail slow approach, if a few bits of content on a page don’t kill the whole page. Instead, render as much of it as possible and log/alert over email, or, log files that your site needs some configuration. To do this, a much better approach is to write defensive code that will do the null checks for you, this code would look something like this:

public static class UmbracoHelper
{
public static string GetStringFromUmbracoPropertyValue(Node node, string propertyName)
{
if (node == null || string.IsNullOrEmpty(propertyName))
return string.Empty;
var property = node.GetProperty(propertyName);
return property == null 
? string.Empty 
: property.Value;
}
}

You can get a property from Umbraco like this:

UmbracoHelper.GetStringFromUmbracoPropertyValue(node, "contentTitle");

Just adding a simple static class that wraps up your dynamic Umbraco calls gives you a lot more flexibility compared to directly accessing the Umbraco API. Now you can pass in any node and a string and know that your web page won’t blow-up if a property doesn’t exist. If you wanted to build on this further, instead of returning ‘null’ you could do some additional logging, or, email so you can have visibility of the problem. Over the years I’ve found emailing an error to the team is better than writing to a log file. When errors get written to log files they seem to get forgotten about.

Testable Approach

We have stage one of a solution, but our code still isn’t testable, which is sad. If you want to have a super flexible testable solution, I wouldn’t create a static class, you should inject the helper method into your class from the constructor (or maybe a property with web forms). To change the code about to be more testable, remove the static references and add an interface to the class. I won’t cover IoC in this tutorial as it’s too big a subject. The theory is on application start-up, you register an IoC container like StructureMap, or, SimpleInjector. When ever a class is loaded with a property, the Ioc container will check if a mapping has been registered and if it has injected it. Ioc Is pretty simple but it can be a bit daunting for a beginner. The code would now look like this:

public class UmbracoHelper : IUmbracoHelper 
{
public tring GetStringFromUmbracoPropertyValue(Node node, string propertyName)
{
if (node == null || string.IsNullOrEmpty(propertyName))
return string.Empty;
var property = node.GetProperty(propertyName);
return property == null 
? string.Empty 
: property.Value;
}
}

With the interface looking like this:

public interface IUmbracoHelper 
{
string GetStringValueFromNode(Node node, string propertyName);
string GetStringFromUmbracoPropertyValue(Node node, string propertyName);
}

You would then inject the interface through your codes constructor and call the helper as follows:

public class MyCode(IUmbracoHelper helper)
{
var node= Node.GetCurrent();
helper.GetStringValueFromNode(node, "contentTitle");
}

Conclusion

The aim of this guide is to try and help people new to Umbraco learn the best ways to architect your project so that updating and maintaining it throughout it’s lifetime isn’t a complete nightmare. By taking the time and wrapping up some Umbraco calls you will de-couple a lot of your system, making it more flexible and testable.

Jon D Jones

Software Architect, Programmer and Technologist Jon Jones is founder and CEO of London-based tech firm Digital Prompt. He has been working in the field for nearly a decade, specializing in new technologies and technical solution research in the web business. A passionate blogger by heart , speaker & consultant from England.. always on the hunt for the next challenge

More Posts

1 reply

Trackbacks & Pingbacks

  1. […] This post is a quick reference about how to convert a date from Umbraco and convert it into a C# DateTime when using dynamic binding. When you work with Umbraco, I strongly recommend using a strongly-typed model approach, to learn why, I suggest you read this article, How To Write Safe Testable Code When Using Dynamic Model Binding With Umbraco MVC. […]

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *