C#無限極分類樹-創建-排序-讀取 用Asp.Net Core+EF實現


今天做一個管理后台菜單,想着要用無限極分類,記得園子里還是什么地方見過這種寫法,可今天找了半天也沒找到,沒辦法靜下心來自己寫了:

首先創建節點類(我給它取名:AdminUserTree):

 1 /// <summary>
 2     /// 無限極節點類
 3     /// </summary>
 4     public class AdminUserTree
 5     {
 6         /// <summary>
 7         /// 節點信息
 8         /// </summary>
 9         public int NodeID { get; set; }
10         /// <summary>
11         /// 節點名稱
12         /// </summary>
13         public string NodeName { get; set; }
14         /// <summary>
15         /// 父節點ID
16         /// </summary>
17         public int ParentID { get; set; }
18         /// <summary>
19         /// 對應的鏈接地址
20         /// </summary>
21         public string Url { get; set; }
22         public int? PermissionID { get; set; }
23         public int? OrderID { get; set; }
24         public string Location { get; set; }        
25         public string comment { get; set; }           
26         public string ImageUrl { get; set; }
27         /// <summary>
28         /// 層級
29         /// </summary>
30         public int level { get; set; }
31         /// <summary>
32         /// 子節點數目(很重要)
33         /// </summary>
34         public int childNumberl { get; set; }
35 
36         /// <summary>
37         ///  子節點 (子節點是一個List)這種用法叫什么?
38         /// </summary>
39         public List<AdminUserTree> childNode { get; set; }
40     }

為無限極分類填充數據,由於考慮到示來管理后台每個頁面都會調用到,這里我為控制器創建了一個基類方法

 1 /// <summary>
 2     /// 管理頁面基類(MVC Controller)
 3     /// </summary>
 4     public class AdminBase: Controller
 5     {
 6         /// <summary>
 7         /// EF數據訪問配置
 8         /// </summary>
 9         private readonly ApplicationDbContext _basecontext;
10 
11         /// <summary>
12         /// 管理菜單 這里是基數,聲明為屬性以便控制器里面可以用到
13         /// </summary>
14         public AdminUserTree leftMenu { get; set; }
15 
16 
17         public AdminBase(ApplicationDbContext context)
18         {
19             _basecontext = context;
20             //初始化無限極分類管理菜單
21             buildtree();
22         }
23 
24         /// <summary>
25         /// 建立無限極節點樹-管理菜單
26         /// </summary>
27         public void buildtree()
28         {
29             AdminUserTree result = new AdminUserTree();
30             //初始化一個節點做為根節點
31             result.NodeID = 0;
32             result.NodeName= "管理員菜單";
33             result.Url = "";
34             result.ParentID = -1;
35             result.Location = "";
36             result.OrderID = 0;
37             result.comment = "";
38             result.ImageUrl = "";
39             result.PermissionID = 0;
40             result.level = 0;
41             result.childNumberl = 0;
42             //把根節點傳遞給遞歸方法去創建子節點
43             result.childNode=BuildMenuTree(result, -1);
44             leftMenu = result;
45         }
46 
47         /// <summary>
48         /// 遞歸創建子節點方法
49         /// </summary>
50         /// <param name="node">要為其分配子節點的父級節點</param>
51         /// <param name="levelID">層級關系</param>
52         /// <returns></returns>
53         protected  List<AdminUserTree> BuildMenuTree(AdminUserTree node, int levelID)
54         {
55             var listtree = _basecontext.Admintree;
56 
57             //從數據庫中取出node節點的全部子節點 條件:m.ParentID==node.NodeID
58             List<AdminUserTree> lt  =  listtree.Where(m => m.ParentID==node.NodeID )
59                                             .Select(m=>new AdminUserTree() {
60                                             NodeID =m.NodeID
61                                             ,NodeName=m.NodeName
62                                             ,Url=m.Url
63                                             ,ParentID=m.ParentID
64                                             ,Location=m.Location
65                                             ,OrderID=m.OrderID
66                                             ,comment=m.comment
67                                             ,ImageUrl=m.ImageUrl
68                                             ,PermissionID=m.PermissionID})
69                                             .ToList();
70 
71             if (lt != null)
72             {
73                 //節點深度
74                 node.level = levelID + 1;
75                 //子節點數量,便於前端遞歸輸出時調用
76                 node.childNumberl = lt.Count;
77                 for (int i = 0; i < lt.Count; i++)               
78                 {
79                     //遞歸調用創建子節點
80                     lt[i].childNode = BuildMenuTree(lt[i], node.level);
81                 }
82                 return lt;
83 
84             }
85             else {
86                 return null;
87             }            
88 
89          }
90         
91     }

控制器(Controller)繼承及調用代碼:

 1   public class AdminTreeController : AdminBase
 2     {
 3         private readonly ApplicationDbContext _context;
 4 
 5         
 6         public AdminTreeController(ApplicationDbContext context):base(context)
 7         {
 8             _context = context;    
 9         }
10 
11         // GET: AdminTree
12         public async Task<IActionResult> Index(int id=1)
13         {
14             var treelist = _context.Admintree;
15             var pageOption = new WeiPagerOption
16             {
17                 CurrentPage = id,
18                 PageSize = 15,
19                 Total = await treelist.CountAsync(),
20                 RouteUrl = "/Admintree/Index",
21                 pageNumStep = 5
22             };
23 
24             //分頁參數
25             ViewBag.PagerOption = pageOption;
26 
27             //無限極分類菜單綁定在這里
28             ViewBag.mainMenu = leftMenu;
29 
30             //返回主要數據
31             return View(await treelist.OrderByDescending(b => b.OrderID).Skip((pageOption.CurrentPage - 1) * pageOption.PageSize).Take(pageOption.PageSize).ToListAsync());
32         }
33

View層代碼:

  1 @model IEnumerable<Hxwei.WebWQSF.Models.AdminTreeModel>
  2 @using Hxwei.WebWQSF;
  3 @using Hxwei.WebWQSF.Controllers;
  4 @using System.Text;
  5 @{
  6     ViewData["Title"] = "菜單管理";
  7 }
  8 @functions
  9 {
 10     public string getAdminMenu(AdminUserTree node)
 11     {
 12         StringBuilder sb = new StringBuilder();
 13 
 14         List<AdminUserTree> ls = node.childNode;
 15         if(ls.Count>0)
 16         {
 17             //遍歷每個子節點以輸出,這里用到了排序ls.OrderBy(m => m.OrderID)
 18             foreach (var r in ls.OrderBy(m => m.OrderID))
 19             {
 20                 if (r.childNumberl > 0)
 21                 {
 22                     //當存在子菜單時的方法,這里會有遞歸調用
 23                     sb.Append("<div class=\"btn-group\">");
 24                     sb.Append("<button type=\"button\" class=\"btn btn-default dropdown-toggle\" data-toggle=\"dropdown\">");
 25                     sb.Append(r.NodeName);
 26                     sb.Append("<span class=\"caret\"></span>");
 27                     sb.Append("</button>");
 28                     sb.Append("<ul class=\"dropdown-menu\" role=\"menu\">");
 29                     //遞歸調用
 30                     sb.Append(getAdminMenu(r));
 31                     sb.Append("</ul>");
 32                     sb.Append("</div>");
 33 
 34                 }
 35                 else
 36                 {
 37                     //當不存在子菜單時輸出
 38                     string ntext = string.Format("<li><a href=\"{0}\">{1}</a></li>",r.Url,r.NodeName);
 39                     sb.Append(ntext);
 40 
 41                 }
 42             }
 43 
 44         }
 45 
 46 
 47         return sb.ToString();
 48     }
 49 }
 50 <div class="row">
 51     <div class="col-md-3 navbar-inverse">
 52         <div class="btn-group-vertical col-md-10">
 53             
 54             <button type="button" class="btn btn-default">@ViewBag.mainMenu.NodeName</button>
 55             @Html.Raw(getAdminMenu(ViewBag.mainMenu));
 56           
 57         </div>
 58 
 59     </div>
 60     <div class="col-md-9">
 61         <h2>Index</h2>
 62 
 63         <p>
 64             <a asp-action="Create">Create New</a>
 65         </p>
 66         <table class="table">
 67             <thead>
 68                 <tr>
 69                     <th>
 70                         @Html.DisplayNameFor(model => model.NodeName)
 71                     </th>
 72                     <th>
 73                         @Html.DisplayNameFor(model => model.ParentPath)
 74                     </th>
 75                     <th>
 76                         @Html.DisplayNameFor(model => model.OrderID)
 77                     </th>
 78                     <th>
 79                         @Html.DisplayNameFor(model => model.Url)
 80                     </th>
 81                     <th></th>
 82                 </tr>
 83             </thead>
 84             <tbody>
 85                 @foreach (var item in Model)
 86                 {
 87                 <tr>
 88                     <td>
 89                         @Html.DisplayFor(modelItem => item.NodeName)
 90                     </td>
 91                     <td>
 92                         @Html.DisplayFor(modelItem => item.ParentPath)
 93                     </td>
 94                     <td>
 95                         @Html.DisplayFor(modelItem => item.OrderID)
 96                     </td>
 97                     <td>
 98                         @Html.DisplayFor(modelItem => item.Url)
 99                     </td>
100                     <td>
101                         <a asp-action="Edit" asp-route-id="@item.NodeID">Edit</a> |
102                         <a asp-action="Details" asp-route-id="@item.NodeID">Details</a> |
103                         <a asp-action="Delete" asp-route-id="@item.NodeID">Delete</a>
104                     </td>
105                 </tr>
106                 }
107             </tbody>
108             <tr><td colspan="5" align="center"><pager pager-option="ViewBag.PagerOption as WeiPagerOption"></pager></td></tr>
109         </table>
110     </div>
111 </div>

最后生成的菜單瀏覽器展示效果如下:

寫了很久簡單三層,最近決定用一下ASP.NET MVC,最近剛了解了一下ASP.NET MVC,目前最新的算是ASP.NET Core MVC,這個例子就是剛剛安裝了VS2017 RC后用ASP.NET Core MVC來實現的。學習階段希望與各位大神共勉,有不足的地方請多多指教!謝謝!

在做完這個類子后,我覺得后續還有可以優化的地方,我是從這幾個方面考慮的,希望高手給予指點:

1.這里在構建無限極分類樹時我是多次調用數據庫查詢,如果數據量小的話想着是把數據一次取出然后傳遞后遞歸方法進行操作;由於用了EF,對於EF我也是個新手,只是剛剛會用,不知道EF本身會不會對這種類型的操作進行優化及數據緩存。

2.第二個方面是在無限極分類樹數據真充好后由於每個管理頁面都要調用這個樹的數據,考慮要對其進行緩存,如何緩存是我下一步要考慮的方法;

3.同時每個節點的權限不同,由於每個用戶角色的不同權限所能調用的菜單功能也不一致,這就存在了是為每一個用戶都緩存一棵樹還是全局共享一棵樹的問題,顯然前者是不科學的,應該是全員共享一棵樹的數據,只需要在View層顯示時加以權限判斷就可以了。這也是我在下一步要考慮的。

后續會先解決以上提到第2和第3方面的問題,等我寫好后再把代碼分享出來,大家一起討論!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM