x First time here? Check out the FAQ

Useful helper extension methods (LINQ & null safe access)

    The following article is somewhat obsolete with the advent of uQuery and uQL.

    uQuery comes with the uComponents package. Just install it and you are ready to go.
    uQL is a query DSL/Framework for umbraco. It uses the same design pattens as LINQ to query over umbraco's in-memory xml cache. (UQL = umbraco query language)

    After working on a project or two we've created some extension methods that let you quickly create concise queries for most of the information you need in umbraco in a 'more' typesafe way.

    Note: This does not create an optmised query like LINQ to Umbraco will do, so use with care on large datasets.

    If you have any corrections or suggestions please make them, and drop me a note ( murray at terabyte co nz )

    Here are some example queries:

    // ---Children of a certain doctype---
    node.AllChildren().Where(n => n.NodeTypeAlias == "myNodeTypeAlias");
    // ---Grand children---
    node.AllDescendants().Where(n => (node.Level() - n.Level()) == 2);
    // ---Not hidden children---
    node.AllChildren().Where(n => !n.GetPropertyAsBoolean("umbNaviHide"));
    // ---Children where a date property is today---
    node.AllChildren().Where(n =>
    DateTime.Now.Date == n.GetPropertyAsDateTime("myDateProperty").Date);

    Here are the extension methods:

    // Open Licence copy to be inserted.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using umbraco.presentation.nodeFactory;
    using System.Xml.Linq;

    namespace Terabyte.Umbraco.Extensions
    {
    public static class ExtensionMethods
    {
    /// <summary>
    /// Tell me the level of this node (0 = root)
    /// updated from Depth and changed to start at 0
    /// to align with other 'Level' methods (eg xslt)
    /// </summary>
    /// <param name="n"></param>
    /// <returns></returns>
    public static int Level(this Node n)
    {
    return n.Path.Split(',').Length-1;
    }


    /******* LINQ QUERYABLE *******/
    /// <summary>
    /// Make the All Descendants LINQ queryable
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    public static IEnumerable<Node> AllDescendants(this Node node)
    {
    foreach (Node child in node.Children)
    {
    yield return child;

    foreach (Node grandChild in child.AllDescendants())
    yield return grandChild;
    }
    }

    /// <summary>
    /// Make the imediate Children LINQ queryable
    /// Performance optimised for just imediate children.
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    public static IEnumerable<Node> AllChildren(this Node node)
    {
    foreach (Node child in node.Children)
    {
    yield return child;
    }
    }

    /******** Null Checks *********/
    /// <summary>
    /// Checks the specified field on this node to ensure it has a not-empty value
    /// </summary>
    public static bool HasValue(this Node node, string alias)
    {
    return node.HasValues(new string[] { alias });
    }

    /// <summary>
    /// Checks the specified fields on this node to ensure they ALL have a not-empty value
    /// </summary>
    /// <param name="node"></param>
    /// <param name="aliases">collection of aliases, each named field is checked for non-empty content</param>
    /// <returns></returns>
    public static bool HasValues(this Node node, IEnumerable<string> aliases)
    {
    if (node == null)
    return false;

    foreach (string key in aliases)
    {
    if (node.GetProperty(key) == null || string.IsNullOrEmpty(node.GetProperty(key).Value))
    return false;
    }
    return true;
    }

    /******** Null Safe, Type Converting Property Accessors *********/


    /// <param name="node"></param>
    /// <param name="alias">document type alias, each named field is checked for non-empty content</param>
    /// <param name="cropAlias">image cropper datatype crop name, each named field is checked for non-empty content</param>
    public static string GetPropertyAsImageCropUrl(this Node node, string alias, string cropAlias)
    {
    if (node.GetProperty(alias) == null)
    return string.Empty;

    string cropsXml = umbraco.library.GetXmlNodeById(node.Id.ToString()).Current.OuterXml;
    if (string.IsNullOrEmpty(cropsXml))
    return string.Empty;

    XElement xml = XElement.Parse(cropsXml);
    XElement crop = (from el in xml.Descendants("crop")
    where (string)el.Attribute("name") == cropAlias
    select el).FirstOrDefault();
    if (crop == null)
    return string.Empty;
    return (string)crop.Attribute("url");
    }

    public static string GetPropertyAsString(this Node node, string alias)
    {
    if (node.GetProperty(alias) == null)
    return string.Empty;

    return node.GetProperty(alias).Value;
    }


    /// <summary>
    /// Null property or value returns false
    /// </summary>
    /// <param name="node"></param>
    /// <param name="alias"></param>
    /// <returns></returns>
    public static bool GetPropertyAsBoolean(this Node node, string alias)
    {
    if (node.GetProperty(alias)== null)
    return false;

    string rawvalue = node.GetProperty(alias).Value;

    if (string.IsNullOrEmpty(rawvalue))
    return false;

    if (rawvalue == "1")
    return true;

    bool outvalue = false;
    Boolean.TryParse(rawvalue.ToLower(), out outvalue);
    return outvalue;
    }

    /// <summary>
    /// NOTE: Null == DateTime.MinValue;
    /// </summary>
    /// <param name="n"></param>
    /// <param name="propertyAlias"></param>
    /// <returns></returns>
    public static DateTime GetPropertyAsDateTime(this Node n, string propertyAlias)
    {
    if (n.GetProperty(propertyAlias) != null)
    {
    try
    {
    return (!string.IsNullOrEmpty(n.GetProperty(propertyAlias).Value))
    ? (Convert.ToDateTime((string)n.GetProperty(propertyAlias).Value))
    : DateTime.MinValue;
    }
    catch (FormatException e)
    {
    throw new FormatException(string.Format("Node '{0}' (id:{1}) property: {2} does not contain a valid date, it conains '{3}'",
    n.Name, n.Id, propertyAlias, n.GetProperty(propertyAlias).Value));
    }
    }
    else
    return DateTime.MinValue;
    }

    /// <summary>
    /// NOTE: Null == 0;
    /// </summary>
    /// <param name="n"></param>
    /// <param name="propertyAlias"></param>
    /// <returns></returns>
    public static decimal GetPropertyAsDecimal(this Node n, string propertyAlias)
    {
    if (n.GetProperty(propertyAlias) != null)
    {
    try
    {
    return (!string.IsNullOrEmpty(n.GetProperty(propertyAlias).Value))
    ? (Convert.ToDecimal((string)n.GetProperty(propertyAlias).Value))
    : 0;
    }
    catch (FormatException e)
    {
    throw new FormatException(string.Format("Node '{0}' (id:{1}) property: {2} does not contain a valid decimal, it conains '{3}'",
    n.Name, n.Id, propertyAlias, n.GetProperty(propertyAlias).Value));
    }
    }
    else
    return 0;
    }

    /// <summary>
    /// NOTE: Null == 0;
    /// </summary>
    /// <param name="n"></param>
    /// <param name="propertyAlias"></param>
    /// <returns></returns>
    public static int GetPropertyAsInt(this Node n, string propertyAlias)
    {
    if (n.GetProperty(propertyAlias) != null)
    {
    try
    {
    return (!string.IsNullOrEmpty(n.GetProperty(propertyAlias).Value))
    ? (Convert.ToInt32((string)n.GetProperty(propertyAlias).Value))
    : 0;
    }
    catch (FormatException e)
    {
    throw new FormatException(string.Format("Node '{0}' (id:{1}) property: {2} does not contain a valid int(32), it conains '{3}'",
    n.Name, n.Id, propertyAlias, n.GetProperty(propertyAlias).Value));
    }
    }
    else
    return 0;
    }

    /// <summary>
    /// If the alias value contains node IDs,
    /// this will return a node object created from the first valid ID.
    /// </summary>
    public static Node GetPropertyAsNode(this Node node, string alias)
    {
    if (!node.HasValue(alias))
    return null;

    return node.GetProperty(alias).ToNode();
    }


    /// <summary>
    /// If the alias value contains node IDs,
    /// return a node collection created from valid IDs in the csv.
    /// </summary>
    public static IEnumerable<Node> GetPropertyAsNodeCollection(this Node node, string alias)
    {
    return node.GetProperty(alias).ToNodeCollection();
    }

    /******** Property Value Type Conversions ***********/
    /// <summary>
    /// If the property's value contains (a) node ID
    /// return a node object created from the first valid ID.
    /// </summary>
    /// <param name="property"></param>
    /// <returns></returns>
    public static Node ToNode(this Property property)
    {
    if (property == null)
    return null;

    foreach (Node pickedNode in property.ToNodeCollection())
    {
    return pickedNode; // just take the first one.
    }
    return null;
    }


    /// <summary>
    /// if this property's value contains node IDs,
    /// return a node collection created from valid IDs in the csv.
    /// </summary>
    /// <param name="property"></param>
    /// <returns></returns>
    public static IEnumerable<Node> ToNodeCollection(this Property property)
    {
    if (property == null)
    yield break;

    foreach (string s in property.Value.Split(','))
    {
    int id;
    if (int.TryParse(s, out id))
    {
    Node pickedNode = new Node(id);
    if (pickedNode.Id != 0)
    yield return pickedNode;
    }
    }
    }
    }
    }