5
\$\begingroup\$

I am using jsTree v1.0 (latest v3.0, they withdraw support from older versions) to display the hierarchy in my application. It was good until until I started showing groups/user up to 200 levels, but now I have a new client which has levels as big as up to 5000, so this code is taking too long to execute. I have two options: either I switch to new plugin (time taking) or optimize the existing code which I am using.

public static List<JsTreeNode> NodeList(DataTable dtMainGroup, DataTable dtMainUser, bool includeUsers)
{
    List<JsTreeNode> nodeList = new List<JsTreeNode>();
    List<String> LStrGpId = new List<string>();
    List<String> LStrChild = new List<string>();

    DataView dvGroup = new DataView();
    DataView dvUser = new DataView();

    dvGroup = dtMainGroup.DefaultView;
    dvUser = dtMainUser.DefaultView;

    if (dtMainGroup.Rows.Count > 0)
    {
        for (int i = 0; i < dtMainGroup.Rows.Count; i++)
        {
            DataRow drMain = dtMainGroup.Rows[i];
            if (!LStrGpId.Contains(Convert.ToString(drMain["GroupId"])) && !LStrChild.Contains(Convert.ToString(drMain["GroupId"])))
            {
                if (i == 0)
                {
                    selectedGroup = Convert.ToString(drMain["GroupId"]);
                }
                JsTreeNode node = new JsTreeNode();
                node.attr = new Attributes();
                node.attr.id = Constants.GroupPrefix + Convert.ToString(drMain["GroupId"]);
                node.attr.rel = Constants.treeNodeRelFolder;
                node.data = new Common.Data();
                node.data.title = Convert.ToString(drMain["Name"]);
                node.data.state = Constants.treeNodeStateClosed;

                LStrGpId.Add(Convert.ToString(drMain["GroupId"]));
                node.children = new List<JsTreeNode>();
                if (includeUsers)
                {
                    node = GetUser(dvUser, dvGroup, drMain, node);
                }
                node = GetChild(dvGroup, dvUser, drMain, node, LStrChild, includeUsers);

                nodeList.Add(node);
            }
        }
    }
    return nodeList;
}

public static JsTreeNode GetUser(DataView dvUsr, DataView dvGp, DataRow dr, JsTreeNode node)
{
    dvUsr.RowFilter = "GroupId = '" + Convert.ToString(dr["GroupId"]) + "'";
    if (dvUsr.Count > 0)
    {
        DataTable dtUser = new DataTable();
        dtUser = dvUsr.ToTable();
        for (int k = 0; k < dtUser.Rows.Count; k++)
        {
            DataRow drUser = dtUser.Rows[k];
            JsTreeNode cnode = new JsTreeNode();
            cnode.attr = new Attributes();
            cnode.attr.id = Constants.EmployeePrefix + Convert.ToString(drUser["UserID"]);
            cnode.attr.rel = Constants.treeNodeRelDefault;
            cnode.data = new Common.Data();
            cnode.data.title = Convert.ToString(drUser["FirstName"]);
            cnode.data.state = Constants.treeNodeStateClosed;

            node.children.Add(cnode);
        }
    }
    return node;
}

public static JsTreeNode GetChild(DataView dvGp, DataView dvUsr, DataRow dr, JsTreeNode node, List<string> LStrChild, bool includeUsers)
{
    dvGp.RowFilter = "ParentGroupId = '" + Convert.ToString(dr["GroupId"]) + "'";

    if (dvGp.Count > 0)
    {
        DataTable dtChild = new DataTable();
        dtChild = dvGp.ToTable();

        for (int j = 0; j < dtChild.Rows.Count; j++)
        {
            DataRow drChild = dtChild.Rows[j];

            LStrChild.Add(Convert.ToString(drChild["GroupId"]));

            JsTreeNode cnode = new JsTreeNode();
            cnode.attr = new Attributes();
            cnode.attr.id = Constants.GroupPrefix + Convert.ToString(drChild["GroupId"]);
            cnode.attr.rel = Constants.treeNodeRelFolder;
            cnode.data = new Common.Data();
            cnode.data.title = Convert.ToString(drChild["Name"]);
            cnode.data.state = Constants.treeNodeStateClosed;
            cnode.children = new List<JsTreeNode>();
            if (includeUsers)
            {
                cnode = GetUser(dvUsr, dvGp, drChild, cnode);
            }
            cnode = GetChild(dvGp, dvUsr, drChild, cnode, LStrChild, includeUsers);

            node.children.Add(cnode);
        }
    }
    return node;
}
\$\endgroup\$
2
  • \$\begingroup\$ Just to clarify: is the tree 5000 levels deep? Also, is the performance problem mainly with the server-side processing, client-side rendering, or both? \$\endgroup\$ Nov 10, 2014 at 11:56
  • \$\begingroup\$ @200_success- Yes tree is 5000 level deep. Performance issue is on both side. \$\endgroup\$ Nov 10, 2014 at 12:02

2 Answers 2

6
\$\begingroup\$

I don't think you'll find a single line of C# or JavaScript that will make this run faster. You need lazy loading. When the page first loads, load just the first two levels. Then clicking on a level will load the next level.

If the data in the database doesn't change very often, cache the HTML snippets sent back to the browser for a bigger boost to performance. There are only so many code changes you can make before you are just plain old trying to process to much data at once.

\$\endgroup\$
4
\$\begingroup\$

I'm not great at answering performance queries, but I'll address the other aspects of your code.

Naming

Your casing is fine here, but I would avoid using Hungarian notation. You can get your point across fine without it.

So:

DataView dvGroup = new DataView();

becomes:

DataView group = new DataView();

Additionally, you should avoid including type information in a variable name. If you come to change the type, you'll have to be sure to change the name too.

So:

List<JsTreeNode> nodeList = new List<JsTreeNode>();

Should become:

List<JsTreeNode> nodes = new List<JsTreeNode>();

Declaration

Prefer to use var when the right-hand side of a variable declaration makes its type obvious. This way, should you need to change the type, you only have to do it once.

So:

List<JsTreeNode> nodeList = new List<JsTreeNode>();

Should be:

var nodeList = new List<JsTreeNode>();

Additionally, you can make your code more concise by using Object Initializers, so:

node.attr = new Attributes();
node.attr.id = Constants.GroupPrefix + Convert.ToString(drMain["GroupId"]);
node.attr.rel = Constants.treeNodeRelFolder;

becomes:

node.attr = new Attributes()
{
    id = Constants.GroupPrefix + Convert.ToString(drMain["GroupId"]),
    rel = Constants.treeNodeRelFolder
};

Magic Strings

I'm seeing a couple of magic strings here and there, that should be constants somewhere in your solution.

e.g.

node.attr.id = Constants.GroupPrefix + Convert.ToString(drMain["GroupId"]);

Here "GroupId" should be assigned to a constant variable with a descriptive name, so that it can be changed centrally and also offer a better clue as to its purpose.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.