Click here to Skip to main content
15,166,587 members
Articles / Web Development / ASP.NET
Posted 26 Feb 2008


43 bookmarked

Building Trees from Lists in .NET

Rate me:
Please Sign up or sign in to vote.
4.61/5 (18 votes)
1 Mar 2008CPOL5 min read
An interface to simplify creating trees from lists of database rows or objects

Who Is This Article For?

This article is aimed at .NET developers who need to build a tree (or forest) of objects by loading an array or list of rows from a database, and converting them to a tree structure in memory to use or display.


Storing hierarchical data in a database is a very common requirement, whether it be product categories, sports tournaments, or staff hierarchies. Over the years, I have had to create, store in a database and display trees many times, and my method has evolved from having fixed-size hierarchies, to abstract classes providing tree functionality, and finally to the method described in this article. This article describes a very simple way to load data from a table, and convert it into a tree in C# code for .NET 2.0+.

While other articles have been written on the subject, the following goals are not always met:

  • You should not need to extend an abstract class, because your data class may already extend another class
  • You shouldn't need to convert your class to be a partial class
  • You shouldn't need to change your database table: as long as it uses the standard parent ID column referring to its own primary key, it should work
  • There shouldn't need to be any type-casting of parent or child objects
  • It should be easy to use

The answer is to define an interface - in this case called ITreeNode<t /> - and then have a utility method to build a tree from that. This article assumes that you have a class defined to hold a single node (e.g. a Category object), and that when you get a list of nodes from the database each reference to a parent has been instantiated as an object with its ID set, but without a reference to the fully populated parent object, nor its children.

An Example

Let's look at the very common "Category" entity, used to categorise products. The database table looks like the following:

Table definition of a Category

Let's assume that there are just 8 rows in the database, which break the products into two main categories - "Hardware" and "Software" - and then further sub-categories. In this case "Operating Systems" and "Developer Tools" come under "Software"; "Monitors" and "Peripherals" under "Hardware", and finally "Keyboards" and "Mice" under "Peripherals", giving the following category hierarchy:

Example category hierarchy

In the database, the rows are as follows:

Category table rows

In order to display this information on a website, you create a "Category" class:

public class Category
    private int _id;
    private string _name;
    private Category _parent;
    private List<Category> _children;
    public int Id
        get { return _id; }
        set { _id = value; }

    public string Name
        get { return _name; }
        set { _name = value; }

    public Category Parent
        get { return _parent; }
        set { _parent = value; }

    public List<Category> Children
        get { return _children; }
        set { _children = value; }

A method is also needed to retrieve all the categories from the database. This will be a "flat" list of each category, i.e. the _parent field will point to an object which only has its ID populated, and _children will be null. A possible example of this method is shown here:

static List<Category> GetListFromDatabase(DbConnection con) {
    DbCommand cmd = con.CreateCommand();
    cmd.CommandText = "SELECT Id, Name, ParentID FROM Category";
    cmd.CommandType = CommandType.Text;
    DbDataReader reader = cmd.ExecuteReader();
    List<Category> categories = new List<Category>(); 
    foreach (DbDataRecord row in reader) {
        Category c = new Category();
        c.Id = (int)row["Id"];
        c.Name = (string)row["Name"];
        if (row["ParentID"] != DBNull.Value)
            c.Parent = new Category();
            c.Parent.Id = (int)row["ParentID"];
    return categories;

Once having a list of objects in memory, the ITreeNode interface comes in handy. The first step is to implement this interface:

public class Category : ITreeNode<Category> {
// contents of class remain as above, because the 
// interface is implemented by the Id, Parent, and 
// Children properties

The interface requires that we have one property referring to the parent of the category (where null represents a root-level node), and an IList pointing to the children.

Now we can call the TreeHelper utility methods to convert the flat array returned from GetListFromDatabase into a fully populated hierarchy:

IList<Category> topLevelCategories = 

The variable topLevelCategories contains two Categories: "Software" and "Hardware".

Printing Out All Nodes using Nested HTML <ul> and <li> Tags

Using a recursive method, you can easily print out, for example, the full category hierarchy in nested <ul> tags, as follows:

void Page_Load(object sender, EventArgs e) {
    IList<Category> topLevelCategories = 
    foreach(Category topLevelCategory in topLevelCategories) {

void RenderCategory(Category category) {
    Response.Write("<li>" + category.Name);
    if (category.Children.Count > 0) {
        foreach(Category child in category.Children) {

This will render the following output:

  • Software
    • Operating Systems
    • Developer Tools
  • Hardware
    • Monitors
    • Peripherals
      • Keyboards
      • Mice

Searching For a Single Category in the Tree

// in a website, this may use the ASP.NET Cache object.
List<Category> categories = GetCategories();
int categoryId = int.Parse(Request.Params["categoryId"]); 
Category currentCategory = 
    TreeHelper.FindTreeNode(categories, categoryId);

Printing Breadcrumbs

Continuing the example above, this is how the bread crumbs for the current category could be printed:

Category currentCategory = GetCategory();
foreach(Category category in 
    Response.Write(" / " + category.Name);

If the current category was "Keyboards", this would render the following HTML:

/ Hardware / Peripherals / Keyboards

Tree Helper

The TreeHelper utility class contains numerous other useful methods - such as GetDepth and HasHierarchyLoop - and iterators - such as DepthFirstTraversal, BreadthFirstTraversal, ClimbToRoot, FromRootToNode, and Siblings.

Check out the fully-documented source code for the full details.

Using Extension Methods and "LINQ to Trees"

If you are using a .NET 3.5 solution, you are able to take advantage of extension methods. This has the effect of implementing methods in interface declarations (which is not possible in older versions of C#), which is probably the most useful aspect of extension methods, and indeed was the reason they were invented.

An example using extension methods:

List<Category> categories = GetCategories().ConvertToForest();
Category current = categories.FindCategory(3);
foreach(Category descendent in current.DepthFirstTraversal()) {
    Response.Write("Depth of " + descendent.Name + ": " + descendent.GetDepth();

Remember, ConvertToForest, FindCategory, DepthFirstTraversal and GetDepth are not implemented by the Category class, it simply "inherits" these methods from the TreeHelper class, simply by implementing ITreeNode<T>.

Extension methods go hand-in-hand with LINQ. Yes, strictly speaking, this is simply "LINQ to Objects" rather than "LINQ to trees", but regardless, it is a new way to query your trees:

List<Category> categoryList = Category.GetCategories();

// Get all categories which are not top level categories, 
// and retrieve only the name.
var nonRootCategories = 
    from c in categoryList.DepthFirstTraversalOfList()
    where c.Parent != null
    select new { Name = c.Name };

// Get all categories at Depth 2, ordered by name, and
// get the whole category object.
var level2Categories = 
    from c in categoryList.DepthFirstTraversalOfList()
    where c.GetDepth() == 2
    orderby c.Name ascending
    select c;

Some Cool Stuff

The following .NET language features have made this class much more useful:

  • Interfaces using Generic parameters. Note that the interface definition is ITreeNode<T>, and the reference to the parent, for example, is T Parent. This means you never need to cast from ITreeNode to your class.
  • Creating iterators using the "yield" keyword. This is perhaps one of the more under-rated features introduced in .NET 2.0 which makes creating Iterators so easy. Check out the methods in the TreeHelper<T>.Iterators class.
  • Extension methods and LINQ. Querying trees with LINQ can certainly make certain tasks much easier... and fun.


  • 27th February, 2008: Initial release
  • 2nd March 2008: Changed TreeHelper from a generic class to non-generic class with generic methods (to allow type method inference), and added the section on LINQ.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Daniel Flower
Software Developer
China China
Daniel has a Bachelor of Science with First Class Honours from the University of Auckland, and has designed and developed software in companies large and small.

Comments and Discussions

QuestionHelo Friend Pin
Member 1490173830-Jul-20 1:48
MemberMember 1490173830-Jul-20 1:48 
QuestionExcellent paper, It's really help me a lot for my current project. Pin
chengclw16-Jun-17 15:42
professionalchengclw16-Jun-17 15:42 
QuestionTreeHelper.FindDescendant Pin
Aguzz27-Mar-15 0:36
MemberAguzz27-Mar-15 0:36 
QuestionGreat posting! Pin
Gabriel Toríz Mejía30-Apr-14 10:53
MemberGabriel Toríz Mejía30-Apr-14 10:53 
QuestionTreeHelper code Pin
ralph.e.owens28-Aug-13 9:35
Memberralph.e.owens28-Aug-13 9:35 
AnswerRe: TreeHelper code Pin
Daniel Flower3-Sep-13 4:59
MemberDaniel Flower3-Sep-13 4:59 
GeneralRe: TreeHelper code Pin
ralph.e.owens24-Sep-13 9:30
Memberralph.e.owens24-Sep-13 9:30 
QuestionExpose your Tree Structure as WCF Data Contract Pin
Member 954892528-Oct-12 23:05
MemberMember 954892528-Oct-12 23:05 
QuestionCategory class body after implementation of the ITreeNode interface Pin
Member 39308187-Aug-12 1:10
MemberMember 39308187-Aug-12 1:10 
AnswerRe: Category class body after implementation of the ITreeNode interface Pin
Daniel Flower10-Aug-12 21:13
MemberDaniel Flower10-Aug-12 21:13 
GeneralHi, Have you tried it with LINQ? How did you solve this part? Pin
hcoder 190013-Jun-11 3:53
Memberhcoder 190013-Jun-11 3:53 
GeneralRe: Hi, Have you tried it with LINQ? How did you solve this part? Pin
hcoder 190013-Jun-11 21:23
Memberhcoder 190013-Jun-11 21:23 
QuestionHow to implement ASP.NET TreeView using this utility [modified] Pin
JoshTheFlame28-Jul-10 2:00
MemberJoshTheFlame28-Jul-10 2:00 
GeneralImplementation help Pin
JoshTheFlame18-May-10 22:50
MemberJoshTheFlame18-May-10 22:50 
GeneralRe: Implementation help Pin
Daniel Flower18-May-10 23:45
MemberDaniel Flower18-May-10 23:45 
GeneralRe: Implementation help Pin
JoshTheFlame19-May-10 1:02
MemberJoshTheFlame19-May-10 1:02 
GeneralRe: Implementation help Pin
Daniel Flower19-May-10 1:34
MemberDaniel Flower19-May-10 1:34 
GeneralRe: Implementation help Pin
JoshTheFlame19-May-10 1:49
MemberJoshTheFlame19-May-10 1:49 
GeneralRe: Implementation help Pin
hcoder 190019-May-10 21:04
Memberhcoder 190019-May-10 21:04 
GeneralWith StringBuilder Rendering ... Pin
hcoder 190018-May-10 19:48
Memberhcoder 190018-May-10 19:48 
GeneralRe: With StringBuilder Rendering ... Pin
Daniel Flower18-May-10 23:32
MemberDaniel Flower18-May-10 23:32 
GeneralI have a problem, please help! Pin
MamukaHachidze2-Dec-09 13:14
MemberMamukaHachidze2-Dec-09 13:14 
GeneralRe: I have a problem, please help! Pin
Daniel Flower3-Dec-09 22:22
MemberDaniel Flower3-Dec-09 22:22 
GeneralSome correction Pin
okoji Cyril1-May-09 7:54
Memberokoji Cyril1-May-09 7:54 
GeneralDuplicate Node Names... Pin
Abbas Mousavi4-Mar-09 3:39
MemberAbbas Mousavi4-Mar-09 3:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.