2012-07-09 50 views
2

我试图在.NET 2.0(没错,2.0)的应用程序来填充分层数据而现在升级为假表(所以没有LINQ,LINQ桥,或其他东西)。有没有更好的方法来将SQL中的分层数据填充到类结构,C#或VB.NET中?

我不知道是否有更好的方法来填充分层数据到这个阶层结构?我很确定有一个更好的方法来完成这个任务。

这将是非常高兴看到一个好办法做到这一点。如果任何人有时间展示.NET 2.0的方式,并且如果有其他方式,他们会在.NET 4.0+中做到这一点,那将非常棒。

这里是节点类型结构的一个例子:

using System.Collections.Generic; 

public class ExampleNode 
{ 

private int _id; 

private Nullable<int> _parentId; 


private int _depth; 

private List<ExampleNode> _children = new List<ExampleNode>(); 

public ExampleNode() 
{ 
} 

public virtual int ApplicationNumber { 
    get { return _id; } 
    set { _id = value; } 
} 

public virtual Nullable<int> ParentId { 
    get { return _parentId; } 
    set { _parentId = value; } 
} 


public virtual int Depth { 
    get { return _depth; } 
    set { _depth = value; } 
} 


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

这里是正被使用来填充所述节点结构的一例的功能。看来这不是实现这一目标的最佳方式,它有可能不会填充孙子类型数据。 Depth从存储过程中返回,作为层级中的级别(0级别的项目是顶级,如果某个节点是顶级节点的子级,则它位于级别1,顶级节点的孙级是级别2等)

public List<ExampleNode> GetNodes() 
{ 
// This may not be optimal. 

List<ExampleNode> nodeList = new List<ExampleNode>(); 
Dictionary<int, ExampleNode> nodeDictionary = new Dictionary<int, ExampleNode>(); 

using (SqlDataReader reader = SqlHelper.ExecuteReader(ConfigurationManager.ConnectionStrings("SqlServer").ConnectionString, CommandType.StoredProcedure, "proc_GetNodeStructure", new SqlParameter("@UserId", userId), new SqlParameter("@NodeTypeId", nodeType))) { 
    while (reader.Read) { 
     ExampleNode nodeInstance = new ExampleNode(); 

     nodeInstance.Id = Convert.ToInt32(reader("Id")); 
     nodeInstance.Depth = Convert.ToInt32(reader("Depth")); 


     if (!Information.IsDBNull(reader("ParentId"))) { 
      nodeInstance.ParentId = Convert.ToInt64(reader("ParentId")); 
     } 

     // Add to list 
     nodeList.Add(nodeInstance); 

     // Add to dictionary 
     nodeDictionary.Add(nodeInstance.Id, nodeInstance); 

    } 
} 

foreach (ExampleNode item in nodeList) { 
    if (item.ParentId.HasValue) { 
     nodeDictionary(item.ParentId).Children.Add(item); 
    } 

} 

for (int i = nodeList.Count - 1; i >= 0; i += -1) { 
    if (nodeList(i).Depth > 0) { 
     nodeList.RemoveAt(i); 
    } 
} 

return nodeList; 
} 
+0

我不知道很多人会考虑转向LINQ进行“升级”。 – 2012-07-09 02:12:19

+0

您是否必须使用数据读取器?我在想,如果你可以使用数据表并在递归函数中使用它,首先获得根节点('ParentId IS NULL'),然后根据父id值('ParentId = ')等等。使用'LINQ'你有更多的灵活性,但是如果你被限制使用'.NET 2.0',你就不能使用它。 – 2012-07-09 02:12:29

回答

2

如果我理解正确的话,你

  1. 通过列表收集节点到列表和字典
  2. 迭代,并通过字典安排父/子关系
  3. 从列表中删除节点是有一个积极的深度

......它留下包含层次结构中最顶端节点的列表。你的算法对我来说似乎是正确的

前两个操作是O(n)的复杂性在时间和空间相对于节点的数量,这是非常好的!

您正在做的唯一真正低效的事情是在步骤3中从列表中删除元素。因为底层存储是一个向量,所以从列表前面删除元素的代价很高,因为所有其余元素都需要被抄下来。您正试图通过向后遍历列表来最小化此类复制的数量。想象一下,列表的后半部分是父节点,前半部分是子节点。每当删除一个子节点时,每次移除一个子节点时,仍然必须复制原始列表大小的一半。这接近O(n^2)行为。

因此,对于第3步,你至少有两种选择,如果你想在时间来提高性能:

  1. 使包含仅从第一元素的第二列表,其中深度== 0,
  2. 改为使用链接列表,以使删除为O(1)而不是O(n)的性能。

这里是第一个选项的代码:

... 

List<ExampleNode> roots = new List<ExampleNode>(); 
for (int i = 0; i < nodeList.Count; i ++) { 
    if (nodeList[i].Depth == 0) { 
     roots.Add(nodeList[i]); 
    } 
} 
return roots; 

你可能通过计算多少根节点有步骤1或2中,然后初始化第二节省一点时间因此它的容量等于根节点的数量。这将防止在向列表添加元素时不必要的分配和复制基础列表向量。

List<ExampleNode> roots = new List<ExampleNode>(rootCount); 

这同样适用于所述第一nodeList;您可以延迟其构建,直到您知道查询返回的记录数。

+1

非常感谢门罗!这是我需要的反馈/建议。再次感谢! – jon333 2012-07-09 05:57:22

0

怎么样使用NHibernate?它适用于.net 2 plus,所以你可以继续前进。

相关问题