There's an Extension for That

The development team here at Liquid has a wonderful culture.  We have running jokes, make silly banter and rib each other on a regular basis.  We each have “specialties” in the humor department.  Some are masters at dry sarcasm.  Some are masters at self-deprecating humor.  Myself?  My specialties are obscure music references and “Dad Humor.”

Lately though, I’ve become known for a catch phrase that has nothing to do with an old Morrissey lyric or groan-worthy pun.   It’s the phrase “there’s an extension for that.”

It all started on a project where I had found myself noticing another programmer struggling with a set of logic.  I walked over and casually collapsed their 15 lines of code into one – calling a C# extension method I had already written a week prior.  I then told him: “There’s an extension for that.”  

Throughout the project, the pattern repeated itself. Sometimes I’d let him struggle a bit as an educational exercise.  Sometimes I’d end his suffering quickly and painlessly.  But more times than not, the conversation included the familiar phrase: “There’s an extension for that.”

The next thing I knew; the phrase had taken on a life beyond code.  Need some mustard for your sandwich?  “There’s an extension for that.”  Your car needs tires? “There’s an extension for that.” It became one of our many running jokes around the Liquid office.

As much fun as we had with the phrase, it will no doubt start to get old soon – the value of reusable code however does not diminish.

For example, a common task of retrieving a list of images selected in Umbraco involves the following according to the official Umbraco documentation:

@if (Model.Content.HasValue("caseStudyImages"))
var imgList= Model.Content.GetPropertyValue<string>("caseStudyImages").Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse)
var caseStudyImagesCollection = Umbraco.TypedMedia(caseStudyImagesList).Where(x => x != null);

So each time we need to retrieve a list of images from a property in the CMS we must

  1. Check if the property has a value
  2. Split the comma delimited list of IDs
  3. Pass the resulting array of images into the TypedMedia call to return the collection.

Putting that into an extension would look similar to the following:

public static IEnumerable<IPublishedContent> GetMediaCollection(this IPublishedContent c, string propertyName)
            if (c.HasValueSafe(propertyName))
                var listValue = c.GetPropertyValue<string>(propertyName);
                var nodeIdList = listValue.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse);
                return GetHelper().TypedMedia(nodeIdList).Where(x => x != null);
            return Enumerable.Empty()<IPublishedContent>;

…And turn the original example into…

var caseStudyImagesCollection = Model.Content.GetMediaCollection("caseStudyImages");

I’ve always been a fan of extension methods and shared libraries within the team.  They support many admirable goals:  speed, reusability, standardization, maintainability, stability and my personal favorite acronym which wraps these all up into a neat package:  the DRY principle (aka Don’t Repeat Yourself).

And yet – something clicked after that running joke started.  It was the catalyst to formalize our process further.  Rather than snippets or examples, we created publishable and versioned libraries to include in the projects we created. Since then, we have taken many of our helpful extensions, models, helpers, etc. and packaged them up into NuGet packages and placed them in their own Git repos for maintenance as separate entities.

Below are some of the other categories of tools we’ve found useful to have in our library.

CMS-Specific, but common operations:

  • Checking if a property exists
  • Retrieving properties
  • Traversing hierarchies
  • Getting image collections
  • Handling fallback properties
  • Checking site-wide settings
  • Handling site search functions

LINQ extensions:

  • Predicates
  • Comparisons
  • Flattening/traversals

Razor helpers:

  • Custom form input helpers
  • Custom partial caching helpers

Models and Miscellaneous Base Classes:

  • MVC form models
  • MVC custom attributes
  • Content pattern models

The use of shared code allows us to create solutions more efficiently with reliable and proven logic.  Our developers are then free to focus on the truly custom code related to each project rather than reinventing the wheel for each new effort.

We will continue to add to our libraries, and refine what we’ve already put in place.  But the latest challenge is simply this:

People have to know that the code exists!!!

We’ve been exploring different ways to “advertise” the existence of the code.  We need it to be easily searchable and discoverable…

… BEFORE the programmer spends 2 hours trying to figure it out on their own.

… BEFORE that programmer cringes as he hears me whisper

“There’s an extension for that.”