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.Depth() - n.Depth()) == 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 depth of this node.
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
public static int Depth(this Node n)
{
return n.Path.Split(',').ToList().Count;
}
/******* 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;
}
}
}
}
}