We need you!

We're working hard on the next version of Developer Fusion. Let us know what you think we should be up to!

Members

Technology Zones

Articles

Hosted By

MaximumASP

Info

Rated
Read 119,067 times

Contents

Downloads

Related Categories

Tree structures in ASP.NET and SQL Server - Previewing Sub-levels

Previewing Sub-levels

One other common feature of some web directories is to not only list the direct children of the node we're looking at - but also revealing the direct children of those nodes (ie two depths below the current node). Thanks to our lineage and depth columns, with a bit of thought it's easy to return a list of child nodes up to a particular depth.

CREATE PROCEDURE dfTreeGetSubChildren ( @id INT, @depth INT ) AS
SELECT t.* FROM dfTree AS children INNER JOIN dfTree AS actualNode
    ON children.lineage LIKE actualNode.lineage + '_%'
    WHERE actualNode.id = @id AND children.depth - actualNode.depth <= @depth
    ORDER BY children.lineage, children.name

The inner join condition (ON children.lineage LIKE actualNode.lineage + '_%') ensures that any nodes we are returning is a child of the actualNode. The underscore (which matches at least one character) ensures that we don't return the node itself. The WHERE clause specifies that our actualNode should match the @id parameter we were passed, and the difference between the depth of that node, and any node we return should be at most @depth.

In order to add this operation to our provider, we just need the following:

public ArrayList GetSubChildren(int uniqueID, int depth)
{
    return ProcessList("dfTreeGetSubChildren",
        new SqlParameter("@id",uniqueID),
        new SqlParameter("@depth",depth));
}

In our ASP.NET page, we modify the categoryList repeater so that it includes a call to a new function RepeatString that will repeat the string "--" according to the difference between the depth of the node we're displaying, and the actual node we're viewing (currentNode).

<asp:Repeater Runat="server" ID="categoryList">
    <ItemTemplate>
        <%# RepeatString("--",(int)DataBinder.Eval(Container.DataItem,"Depth") - currentNode.Depth - 1) %>
        <a href='<%# "TreeDemoC.aspx?id=" + DataBinder.Eval(Container.DataItem,"UniqueID") %>'
            ><%# DataBinder.Eval(Container.DataItem,"Name") %></a>
    </ItemTemplate>
    <SeparatorTemplate> <br /> </SeparatorTemplate>
</asp:Repeater>

The currentNode variable is declared as

protected TreeNode currentNode;

In the code-behind class. Until now we haven't actually needed a direct reference to the "current node" - but we can do this easily enough, as our call to GetPath already returns us the node we want - at the end of the list:

ArrayList pathList = treeProvider.GetPath(treeID);
// fetch the current node
currentNode = (TreeNode)pathList[pathList.Count - 1];
// continue the binding as we did before
breadcrumb.DataSource = pathList;
breadcrumb.DataBind();
// now bind the list (we need a ref to currentNode first)
categoryList.DataSource = treeProvider.GetSubChildren(treeID,2);
categoryList.DataBind();

After adding a few more rows to the database, your page should look something like this:

A diagram displaying the structure of the table, with these fields: id (int, primary key), parentId (int), name (varchar), depth (int), lineage (varchar).

This could easily be modified to display the entire tree below the current category by simply removing the depth restriction on the data we return. To do this, you can simply comment out the AND t.depth - m.depth <= @depth part of the dfTreeGetSubChildren stored procedure. Then you'd get something that looks like this:

A diagram displaying the structure of the table, with these fields: id (int, primary key), parentId (int), name (varchar), depth (int), lineage (varchar).

Cool, eh?

We've only scratched the surface here of the sorts of queries you can perform over the tree now that we've got the self-maintaining lineage and depth fields. I'm not sure how much interest there will be in this article - but if you find some other useful queries you'd like to share, then just let me know.

James first started writing tutorials on Visual Basic in 1999 whilst starting this website (then known as VB Web). Since then, the site has grown rapidly, and James has written numerous tutorials, articles and reviews on VB, PHP, ASP and C#. In October 2003, James formed the company Developer Fusion Ltd, which owns this website, and also offers various development services. In his spare time, he's a 3rd year undergraduate studying Computer Science in the UK. He's also a Visual Basic MVP.

Comments

  • Re: [4633] Tree structures in ASP.NET and SQL Server

    Posted by toranaga on 11 Mar 2007

    Here is how id did it to use with asp:tree:

    add this methods to the TreeNode class

    public string GetXml
        {
            get
     ...

  • Re: [4633] Tree structures in ASP.NET and SQL Server

    Posted by Goose99 on 24 Apr 2006

    Thanks this article has been a great help, I have one question.


    How hard would it be to get the tree f...

  • Nestedet model

    Posted by jcelko on 04 Nov 2005

    Here is my standard "cut & paste" on the Nested sets model for hierarchies.

    There are many ways to represent a tree or hierarchy in SQL. This is called an adjacency list model and it looks like th...

  • Posted by James Crowley on 03 Nov 2005

    Okay, point taken ... but as far as I'm aware if you moved the code I talk about out of triggers just into stored procs (or whatever), then the SQL is pretty standard stuff?

    I know that you're a ve...

  • Posted by jcelko on 03 Nov 2005

    Yoiu can do your heirarchies & trees in pure declarative SQL without using any proprieary 4GL like PL/SQL, T-SQL, etc.