無限分類 (無限級分類) 常見於商品分類、選單、組織架構。核心資料表結構:id (PK), parent_id (FK), name。本文提供 C# 遞歸實作、非遞歸 (迭代/堆疊) 替代方案、效能優化建議。
CREATE TABLE Category (
id INT PRIMARY KEY IDENTITY,
parent_id INT NULL FOREIGN KEY REFERENCES Category(id),
name NVARCHAR(100) NOT NULL,
sort_order INT DEFAULT 0,
is_active BIT DEFAULT 1
);
public class Category
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Name { get; set; }
public List Children { get; set; } = new List();
}
public List BuildTree(List flatList, int? parentId = null)
{
return flatList
.Where(c => c.ParentId == parentId)
.Select(c => new Category
{
Id = c.Id,
ParentId = c.ParentId,
Name = c.Name,
Children = BuildTree(flatList, c.Id)
})
.OrderBy(c => c.SortOrder)
.ToList();
}
// 使用
var all = db.Categories.Where(c => c.IsActive).ToList();
var tree = BuildTree(all);
<asp:Repeater ID="rptCategory" runat="server">
<ItemTemplate>
<li><%# Eval("Name") %>
<asp:Repeater DataSource='<%# Eval("Children") %>' runat="server">
<ItemTemplate><ul><li><%# Eval("Name") %></li></ul></ItemTemplate>
</asp:Repeater>
</li>
</ItemTemplate>
</asp:Repeater>
public ListBuildTreeIterative(List flatList) { var lookup = flatList.ToLookup(c => c.ParentId); var roots = lookup[null].OrderBy(c => c.SortOrder).ToList(); var stack = new Stack (roots.AsEnumerable().Reverse()); while (stack.Count > 0) { var node = stack.Pop(); node.Children = lookup[node.Id].OrderBy(c => c.SortOrder).ToList(); foreach (var child in node.Children.AsEnumerable().Reverse()) stack.Push(child); } return roots; }
| 策略 | 適用場景 |
|---|---|
路徑枚舉 (path: 1/5/12/) | 頻繁讀取子樹、需快速查詢所有後代 |
巢狀集合 (Nested Sets: lft, rgt) | 靜態樹、大量讀取、極少移動節點 |
遞歸 CTE (SQL Server WITH RECURSIVE) | 資料庫層完成樹查詢、減少網路往返 |
| 快取樹結構 | 讀多寫少、接受最終一致性 |
parent_id 後需重建快取/路徑/巢狀集合值。選擇正確模型 (遞歸/路徑/巢狀集合/CTE) 取決於讀寫比例、樹深度、移動頻率與技術棧。