2012-07-09 60 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,所以你可以繼續前進。

相關問題