This is the second part in a two part post series about my search for a C# alternative for the Ruby’s acts_as_tree plugin. Part one can be found here.

My goal is to use C# extension methods to add functionality for tree traversal to existing (database entity) classes. In part 1, we saw that in Ruby, mixins (like the acts_as_treen Rails plugin) can be used to add functionality to existing classes. Although (strictly speaking), mixins don’t exists in C#, we can achieve something similar to mixins with the help of extension methods.

My starting point was the AsHierarchy extension methods by Stefan Cruysberghs (as explained in part 1). However, I decided to go for a more “Ruby on Rails like” way, which means adding functionality to individual entity classes, instead of a collection of entities. Also, I only wanted to add tree functionality to classes which are somehow marked. This can be achieved by not defining an extension method on a class, but on an interface. Every class that implements that interface, has access to its extension methods. Such an interface, that can be empty and is only used for annotation, is called a marker interface.

public interface IHierarchyNode<T> { }

Note that the IHierarchyNode marker interface takes a type parameter, for reasons I’ll explain further on.

Whether or not the use of marker interfaces is desirable, is subject of debate. Personally, I’m not bothered by an occasional marker interface in my code. Although I’d have preferred to use an attribute instead of an interface as a marker. For example:

[HierarchyNode]
public class Entity {
}

But to my knowledge there is no way to implement extension methods for attributes in C#.

IHierarchyNode extension methods

Now that we’ve defined the IHierarchyNode interface, we can define extension methods for this interface that implement the desired behavior. First, let’s define two methods that retrieve child entities:

/// <summary>
/// Retrieves child entities of current entity from a <see cref="IQueryable"/> datasource.
/// </summary>
/// <typeparam name="TEntity">Entity class</typeparam>
/// <typeparam name="TProperty">Property type of id and parentId</typeparam>
/// <param name="node">Entity that implements the <see cref="IHierarchyNode{T}"/> interface</param>
/// <param name="propertyNameId">Selector for the property that holds the entity id</param>
/// <param name="propertyNameParentId">Selector for property that holds the parent id</param>
/// <returns>Child entities</returns>
public static IEnumerable<TEntity> Children<TEntity, TProperty>(
  this IHierarchyNode<TEntity> node,
  IQueryable<TEntity> allNodes,
  Expression<Func<TEntity, TProperty>> idProperty,
  Expression<Func<TEntity, TProperty>> parentIdProperty) where TEntity : class
{
    ParameterExpression parameter = Expression.Parameter(typeof(TEntity), "e");
    Expression<Func<TEntity, bool>> predicate;

    // Left hand side
    PropertyInfo propInfo = GetPropertyInfo<TEntity, TProperty>(parentIdProperty);
    Expression left = Expression.Property(parameter, propInfo);

    // Right hand side
    PropertyInfo idPropInfo = GetPropertyInfo<TEntity, TProperty>(idProperty);
    object idValue = idPropInfo.GetValue(node, null);
    Expression right = Expression.Constant(idValue);

    predicate = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(left, right), parameter);

    var result = allNodes.Where(predicate);
    return result;
}

/// <summary>
/// Retrieves child entities of current DmModel entity. from the database
/// </summary>
/// <typeparam name="TEntity">Entity class</typeparam>
/// <typeparam name="TProperty">Property type of Id's</typeparam>
/// <param name="node">Entity that implements the <see cref="IHierarchyNode{T}"/> interface</param>
/// <param name="propertyNameId">Selector for the property that holds the entity id</param>
/// <param name="propertyNameParentId">Selector for property that holds the parent id</param>
/// <returns>Child entities</returns>
public static IEnumerable<TEntity> Children<TEntity, TProperty>(
  this IHierarchyNode<TEntity> node,
  Expression<Func<TEntity, TProperty>> idProperty,
  Expression<Func<TEntity, TProperty>> parentIdProperty) where TEntity : DmModel
{
    var allNodes = ((TEntity)node).Context.Query<TEntity>();
    return Children<TEntity, TProperty>(node, allNodes, idProperty, parentIdProperty);
}

Child entities are selected by looking at two properties, specified in the method’s parameters idProperty and parentIdProperty. These properties represent the database columns that contain the id and parent id of an entity.

Note that the second overload is specific for DmModel entities classes. DmModel is the class used for entities in DynamicModel ORM. But the method can be easily adopted for any ORM that supports querying via IQueryable, for example LINQ to SQL or Entity Framework. For EF there is one caveat though; the EF Model does not generate foreign key properties. Stefan Cruysberghs shows in his blog post how to work around this.

Also note the use of lambda expressions to denote properties. On the web, many examples can be found of how to get a property name from a lambda expression, but this is the one I used: How to get property name from lambda expression. This is how these lambda expressions can be used:

var children = node.Children<Entity, int>(i => i.Id, i => i.ParentId)

Because the IHierarchyNode takes a generic type parameter, the compiler can check whether the instance the extension method is called on, is compatible with the input type of id and parentId lambda expressions.

Using the IHierarchyNode extension methods

All classes that implement the IHierarchyNode interface have access to the Children method defined above. Let’s look at an example of a DynamicModel entity class, used to represent posts in a WordPress database. The class definition looks like this:

[DbTable("wp_posts")]
public class WpPost : DmModel, IHierarchyNode<WpPost> {
    private UInt64 _id;

    [DbColumn("ID", PrimaryKey = true)]
    public UInt64 Id
    {
        get { return _id; }
        set { Set(ref _id, value, "Id"); }
    }

    private String _title;

    [DbColumn("post_title")]
    public String Title
    {
        get { return _title; }
        set { Set(ref _title, value, "Title"); }
    }

    private UInt64 _parentId;

    [DbColumn("post_parent")]
    public UInt64 ParentId
    {
        get { return _parentId; }
        set { Set(ref _parentId, value, "ParentId"); }
    }

}

Don’t pay too much attention to the attributes (e.g. “[DbColumn]“), they used needed by DynamicModel ORM; but you can use a classes from any other ORM instead. The only requirement is the existence of properties for id and the parent id values.

Now, suppose we want to retrieve children of page with ID 2 in the WordPress page tree (pages are stored in the same table as posts in WordPress, so we can use the WpPost entity):

// Retrieve root node from database (one query)
WpPost root = context.Find<WpPost>(2);
// Retrieve child nodes for the root
List<WpPost> children = root.Children<WpPost, UInt64>(i => i.Id, i => i.ParentId).ToList();

Because the Children method works on an IQueryable, only those rows are retrieved from the database that are needed to build the tree.

In the same way as with the Children() method, we can define a method that retrieves an entity’s parent:

public static TEntity Parent<TEntity, TProperty>(
  this IHierarchyNode<TEntity> node,
  IQueryable<TEntity> allNodes,
  Expression<Func<TEntity, TProperty>> idProperty,
  Expression<Func<TEntity, TProperty>> parentIdProperty) where TEntity : class
{
    ParameterExpression parameter = Expression.Parameter(typeof(TEntity), "e");
    Expression<Func<TEntity, bool>> predicate;
    PropertyInfo leftProp = GetPropertyInfo<TEntity, TProperty>(idProperty);
    Expression left = Expression.Property(parameter, leftProp);
    PropertyInfo rightProp = GetPropertyInfo<TEntity, TProperty>(parentIdProperty);
    object rightValue = rightProp.GetValue(node, null);
    Expression right = Expression.Constant(rightValue);
    predicate = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(left, right), parameter);
    IEnumerable<TEntity> e = allNodes.Where(predicate);
    return e.FirstOrDefault();
}

How about relational properties in ORMs?

You might wonder if these extension methods are actually necessary when using an ORM, since almost any ORM supports traversing parent/child relationships between entities out of the box via relational properties.

Take for example this entity class from a Lightspeed model:

// Lightspeed ORM entity class example
public class Customer : Entity<int>
{

    public string CustomerID
    {
      get { return _customerID; }
      set { Set(ref _customerID, value); }
    }

    public EntityCollection<Order> Orders
    {
      get { return Get(_orders); }
    }    
}

Lightspeed will automatically fill the “Orders” property with orders associated with that customer. Why not use this behavior to access parent and children via a self-association?

Well, on the background, the acts_as_tree plugin actually does this: it silently adds parent/child relations to an existing ActiveRecord (the default Ruby on Rails ORM) class. This is done automatically upon calling the acs_as_tree method.

Whether to use automatic associations via a mixin-like-approach (acts_as_tree), or manually add association backed properties to your entities (default ORM approach), is a matter of taste. Personally, in the case of tree traversal, I prefer using mixins. Mainly because they allow me to add/remove behavior to a class without actually adding/removing properties or methods in entity classes.

And secondly: entity associations are primary intended be to used for relations between different entity types (database tables), instead of self-associations. Although many ORMs may allow self-associations, for me, it does not feel semantically correct to use associations for the purpose of building a tree from rows in a single table.

Conclusion

Is my C# solution as clean and elegant as Ruby’s acts_as_tree? I’m afraid not. But I’m satisfied with the result anyway. I now have a generic solution to turn any flat IQueryable of database entities into a tree with relative ease. And in the process I learned about mixins, marker interfaces and lambda property expressions as parameters :).

There probably is a more elegant implementation thinkable for the extension methods above. And maybe it is possible to approach Ruby’s mixin behavior in C# in a better way than with extension methods. But I prefer to code in an agile/iterative way, and this is step one. So I’ll keep improving the code above, and maybe there will be a sequel to this post.

Read more

2 Responses to “C# alternative for Ruby’s acts_as_tree, part 2”

  1. anehra63 says:

    Nice and simple code bro thanks for sharing

  1. [...] This post was mentioned on Twitter by Richard Laksana, gerwert. gerwert said: C# .NET alternative for Ruby on Rails' acts_as_tree plugin: http://bit.ly/b60KDf [...]

Leave a Reply





Human Verification