資料庫設計:自關聯階層結構

- MODULE_TYPE (模組分類):
ID、PARENT_ID (自關聯 FK)、NAME、ORDER_ID
- MODULE (模組/葉子節點):
MODULE_TYPE_ID (FK 至分類)、TAG、NAME、MODULE_URL
SQL 建表腳本 (SQL Server 2005)
CREATE TABLE [dbo].[MODULE_TYPE](
[ID] [varchar](15) NOT NULL PRIMARY KEY,
[PARENT_ID] [varchar](15) NULL REFERENCES MODULE_TYPE(ID),
[NAME] [varchar](40) NOT NULL,
[ORDER_ID] [int] NOT NULL
);
CREATE TABLE [dbo].[MODULE](
[ID] [varchar](15) NOT NULL PRIMARY KEY,
[MODULE_TYPE_ID] [varchar](15) NOT NULL REFERENCES MODULE_TYPE(ID),
[TAG] [varchar](40) NOT NULL,
[NAME] [varchar](40) NOT NULL,
[MODULE_URL] [varchar](200) NULL,
[ORDER_ID] [int] NOT NULL
);
-- 範例資料
INSERT INTO MODULE_TYPE VALUES ('0000000001', NULL, '分類 1', 10);
INSERT INTO MODULE_TYPE VALUES ('0000000011', '0000000001', '分類11', 11);
INSERT INTO MODULE_TYPE VALUES ('0000000012', '0000000001', '分類12', 12);
INSERT INTO MODULE_TYPE VALUES ('0000000002', NULL, '分類 2', 20);
-- ... 略
LINQ to SQL 實體類別 (sqlmetal 產生)

MODULE_TYPE 擁有 MODULE_TYPE_MODULE_TYPE (子分類集合) 與 MODULE (模組集合) 導航屬性
MODULE 擁有 MODULE_TYPE 父分類導航屬性
遞歸載入 TreeView 完整程式碼
private void LoadModuleTree()
{
using (var ctx = new DMN.ZXKP())
{
// 1. 取得根分類 (PARENT_ID IS NULL)
var rootTypes = from m in ctx.MODULE_TYPE
where m.PARENT_ID == null
orderby m.ORDER_ID
select m;
TreeNode rootNode = new TreeNode("功能模組");
tvMenu.Nodes.Add(rootNode);
foreach (var type in rootTypes)
{
TreeNode node = new TreeNode
{
Text = type.NAME,
ToolTip = type.NAME
};
rootNode.ChildNodes.Add(node);
AddSubNodes(node, type); // 遞歸
node.Expanded = false;
}
}
}
private void AddSubNodes(TreeNode parentNode, MODULE_TYPE currentType)
{
using (var ctx = new DMN.ZXKP()) // 或傳入共用 DataContext
{
// 2. 遞歸載入子分類
foreach (var subType in currentType.MODULE_TYPE_MODULE_TYPE.OrderBy(t => t.ORDER_ID))
{
TreeNode subNode = new TreeNode
{
Text = subType.NAME,
ToolTip = subType.NAME
};
parentNode.ChildNodes.Add(subNode);
AddSubNodes(subNode, subType); // 繼續遞歸
}
// 3. 載入該分類下的模組 (葉子節點)
foreach (var module in currentType.MODULE.OrderBy(m => m.ORDER_ID))
{
TreeNode leafNode = new TreeNode
{
Text = module.NAME,
ToolTip = module.NAME,
NavigateUrl = module.MODULE_URL // 超連結導航
};
parentNode.ChildNodes.Add(leafNode);
}
}
}
執行效果

效能優化建議
- 單一 DataContext:遞歸過程共用一個
DataContext,避免重複查詢
- 預先載入扁平化:一次性
LoadWith 所有分類與模組,記憶體中遞歸組裝 TreeNode
- 分頁/虛擬化:節點過萬時啟用 TreeView 虛擬模式
- 快取策略:選單結構變動低,適合
HttpRuntime.Cache 或記憶體快取
擴展:支援多語系、權限控管
// 權限過濾範例
var allowedModules = from m in currentType.MODULE
where UserHasPermission(m.TAG)
select m;