2012-02-08 48 views
2

如何讀取SQL數據以獲取分層單元列表?將sql層次讀入c#對象

不取決於僅SQL Server解決方案?

public class Unit { 
    public Unit Parent { get; set; } 
    public int Id { get; set; } 
    public String Name { get; set; } 
} 

List<Unit> list = new List<Unit>(); 

while(reader.Read()) 
{ 
    // read sql data into clr object UNIT 
} 

該表有3列:

Id| ParentId | Name 
1 | Null  | bla 
2 | 1  | x 
3 | 1  | y 
4 | 2  | z 
5 | 2  | test 

UPDATE

That is the code which is taken from user marc_s: 

List<Unit> units = new List<Unit>(); 

      String commandText = 
      @";WITH Hierarchy AS 
       (
       SELECT 
        ID, ParentID = CAST(NULL AS INT), 
        Name, HierLevel = 1 
       FROM 
        dbo.Unit 
       WHERE 
        ParentID IS NULL 

       UNION ALL 

       SELECT 
        ht.ID, ht.ParentID, ht.Name, h1.HierLevel + 1 
       FROM 
        dbo.Unit ht 
       INNER JOIN 
        Hierarchy h1 ON ht.ParentID = h1.ID 
      ) 
       SELECT Id, ParentId, Name 
       FROM Hierarchy 
       ORDER BY HierLevel, Id"; 

      using(SqlConnection con = new SqlConnection(_connectionString)) 
      using (SqlCommand cmd = new SqlCommand(commandText, con)) 
      { 
       con.Open(); 

       // use SqlDataReader to iterate over results 
       using (SqlDataReader rdr = cmd.ExecuteReader()) 
       { 
        while (rdr.Read()) 
        { 
         // get the info from the reader into the "Unit" object 
         Unit thisUnit = new Unit(); 

         thisUnit.Id = Convert.ToInt32(rdr["Id"]); 
         thisUnit.UnitName = rdr["Name"].ToString();      

         // check if we have a parent 
         if (rdr["ParentId"] != DBNull.Value) 
         { 
          // get ParentId 
          int parentId = Convert.ToInt32(rdr["ParentId"]); 

          // find parent in list of units already loaded 
          // NOTE => not needed anymore => Unit parent = units.FirstOrDefault(u => u.Id == parentId); 

          // Instead use this method to find the parent: 


          Unit parent = FindParentUnit(units, parentId); 

          // if parent found - set this unit's parent to that object 
          if (parent != null) 
          { 
           thisUnit.Parent = parent; 
           parent.Children.Add(thisUnit); 
          } 
         } 
         else 
         { 
          units.Add(thisUnit); 
         } 
        } 
       } 
      } 

      return units; 

那是在填充的列表

http://oi41.tinypic.com/rmpe8n.jpg

0123的屏幕截圖

那從單位表的SQL數據:

http://oi40.tinypic.com/mt12sh.jpg

問題

其實填充的列表應該只有一個單元的對象不是11(指數0 - 10)。是的,列表中的第一個單元是正確填充的,但單元索引1 - 10不應該在列表中。

這是應該看起來像真正:

0 
|--1 
| |--3 
| | |--9 
| | |--10 
| |--4 
|--2 
| |--5 
| |--6 
|--7 
|--8 

UPDATE和解決方案要做到這一點是使用對象關係映射器,如實體框架做

private static Unit FindParentUnit(List<Unit> units, int parentId) 
     { 
      Unit parent; 
      foreach (Unit u in units) 
      { 
       if (u.Id == parentId){ 
        return u;          
       } 
       parent = FindParentUnit(u.Children, parentId); 
       if (parent != null) 
        return parent; 
      } 
      return null; 
     } 
+0

如何http://stackoverflow.com/questions/6037501/sql-data-hierarchy: 這裏,如果行是通過ID有序上升,只有工作的例子? – erikxiv 2012-02-08 07:56:18

+0

查看[Wikipedia ON CTE](http://en.wikipedia.org/wiki/Common_table_expressions):*通用表表達式由DB2,Firebird [1],Microsoft SQL Server,Oracle,PostgreSQL,HyperSQL和H2(實驗)* – 2012-02-08 09:25:46

+0

行 - 那有什麼問題?您的「根」人(ID = 0)有**四個孩子** - 正如您提供的數據所預期的那樣。我很確定,如果您深入瞭解根人員的「兒童」收藏,您也可以找到其他節點及其子女。 – 2012-02-16 06:00:05

回答

1

這樣的事情應該做:-)

// set up connection string 
string connectionString = "server=.;database=test;integrated Security=SSPI;"; 

// define a CTE (Common Table Expression) to recursively build your hierarchical 
// structure into a flat list and order it according to its "sequence" (root first) 
string cteStatement = 
      @";WITH Hierarchy AS 
       (
       SELECT 
        ID, ParentID = CAST(NULL AS INT), 
        Name, HierLevel = 1 
       FROM 
        dbo.HierarchyTest -- replace with your table name! 
       WHERE 
        ParentID IS NULL 

       UNION ALL 

       SELECT 
        ht.ID, ht.ParentID, ht.Name, h1.HierLevel + 1 
       FROM 
        dbo.HierarchyTest ht -- replace with your table name! 
       INNER JOIN 
        Hierarchy h1 ON ht.ParentID = h1.ID 
      ) 
       SELECT Id, ParentId, Name 
       FROM Hierarchy 
       ORDER BY HierLevel, Id"; 

// set up list of "Unit" objects 
List<Unit> units = new List<Unit>(); 

// create connection and command to query    
using(SqlConnection conn = new SqlConnection(connectionString)) 
using(SqlCommand cmd = new SqlCommand(cteStatement, conn)) 
{ 
    conn.Open(); 

    // use SqlDataReader to iterate over results 
    using(SqlDataReader rdr = cmd.ExecuteReader()) 
    { 
     while(rdr.Read()) 
     { 
      // get the info from the reader into the "Unit" object 
      Unit thisUnit = new Unit(); 

      thisUnit.Id = rdr.GetInt32(0); 
      thisUnit.Name = rdr.GetString(2); 
      thisUnit.Children = new List<Unit>(); 

      // check if we have a parent 
      if(!rdr.IsDBNull(1)) 
      { 
       // get ParentId 
       int parentId = rdr.GetInt32(1); 

       // find parent in list of units already loaded 
       Unit parent = units.FirstOrDefault(u => u.Id == parentId); 

       // if parent found - set this unit's parent to that object 
       if(parent != null) 
       { 
        thisUnit.Parent = parent; 
        parent.Children.Add(thisUnit); 
       } 
      } 
      else 
      { 
       units.Add(thisUnit); 
      } 
     } 
    } 

    conn.Close(); 
} 

爲你做這項工作?

CTE(公用表表達式)以遞歸方式掃描您的表並建立分層節點列表 - 通過按「層次結構級別」對其進行排序,您可以確保在其子節點顯示之前獲取所有父節點向上(使代碼工作)

更新:好了,所以它似乎想要把節點與沒有父到最終名單 - 這很好(但你卻不能說你想有這樣的方式!!) - 我更新了我的代碼 - 請重新檢查一遍!

+0

我編輯我的問題:請ms sql server independend解決方案:) – Pascal 2012-02-08 08:29:01

+0

@Pascal:CTE ** IS ** ANSI SQL-99標準的一部分 - 它是** NOT ** SQL Server特定的! – 2012-02-08 09:22:30

+0

你在USING語句中做了conn.Close()? – Pascal 2012-02-08 10:03:49

2

的一種方式爲你工作。 This answer到一個類似的冠冕堂皇的EF問題應該指出你在正確的方向。

+0

給了你一個觀點,因爲以後我也會用EF。 – Pascal 2012-02-08 17:05:17

1

您的數據表示樹結構,您只需構建創建根單元並添加樹葉的樹。你可以使用一個Dictionary而不是List來在樹中進行seacrhing,這很簡單。

Dictionary<Int32,Unit> dic = new Dictionary<Int32,Unit>(); 

while(reader.Read()) 
{ 
    //create the new Unit 
    // if the parent is not null get the parent unit from dic 
    // add the new Unit to dic 
} 
+0

你有點不清楚。這是你的意思嗎? Unit unit = new Unit(); If(!DB.IsNull(reader [「parentId」])){ Unit parent = dic [Convert.ToInt32(reader [「parentId」])]; Dic.Add(unit.Id,unit); } – Pascal 2012-02-15 14:18:32