C#無限極分類樹-創建-排序-讀取 用Asp.Net Core+EF實現之方法二:加入緩存機制


在上一篇文章中我用遞歸方法實現了管理菜單,在上一節我也提到要考慮用緩存,也算是學習一下.Net Core的緩存機制。

關於.Net Core的緩存,官方有三種實現:

1.In Memory Caching 我理解是在內容中實現,這種方法適用於單服務器的生產環境。

2.a Distributed Cache 分部式緩存實現。

3.Response Cache 這種方式我理解為客戶端緩存。

今天我只用了第一種實現方法,內存中緩存,之所以用這種方法我是覺得我這里用緩存的初衷是為了減少訪問數據庫的次數,而把訪問數據庫頻率最高的數據轉變為對象而放在緩存里。對於分部式說實話目前我也只是聽說和了解原理,而沒有做過實現,這里先不談它。

微軟官方提供的實現In Memory Caching 方法時有一個微軟標准的示例,GitHub地址是  在這里

示例是用中間件實現的,經過一番學習,用示例里面提到的方法通過中間件實現了緩存,但怎么用到Controller里成了一個難題,也可能是我對中間件的了解不夠深入吧!反正我是不知道該怎么辦了!無耐之下,還是仔細研究了一下代碼發現在中間件里面對緩存的調用(包括設置和取出)其實只在下面這段代碼里實現:

 1 if(!_memoryCache.TryGetValue(cacheKey, out greeting))
 2             {
 3                 // fetch the value from the source
 4                 greeting = _greetingService.Greet("world");
 5 
 6                 // store in the cache
 7                 _memoryCache.Set(cacheKey, greeting,
 8                     new MemoryCacheEntryOptions()
 9                     .SetAbsoluteExpiration(TimeSpan.FromMinutes(1)));
10                 _logger.LogInformation($"{cacheKey} updated from source.");
11             }
12             else
13             {
14                 _logger.LogInformation($"{cacheKey} retrieved from cache.");
15 }
View Code

也就是 _memoryCache.TryGetValue(cacheKey, out greeting)這一段負責從緩存中取出緩存內容;而_memoryCache.Set(cacheKey, greeting, new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(1)));這個方法調用是把數據放到緩存里面的!

知道了這些以后就着手改造我的程序了:

1.創建IAdminTreeService.cs接口類:

public interface IAdminTreeService
    {
        /// <summary>
        /// 全部節點數據樹
        /// </summary>
        AdminUserTree GetAllTreeData { get; }
    }

2.創建服務AdminTreeServices.cs,用於實現從數據庫中取出管理菜單數據,用服務方式實現這種方式還是借鑒了官方示例的實現方式,這種實現是有其道理的。道理自己琢磨吧,最起碼有得用接口統一。不再啰嗦直接代碼:

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

3.管理頁面基類 AdminBase.cs:

 1 public class AdminBase: Controller
 2     {
 3         /// <summary>
 4         /// EF數據訪問配置
 5         /// </summary>
 6         private readonly ApplicationDbContext _basecontext;        
 7         private readonly IAdminTreeService _adminTreeService;
 8         private readonly ILogger<AdminUserTree> _logger;
 9         private readonly IMemoryCache _memoryCache;
10 
11 
12         /// <summary>
13         /// 管理菜單 這里是基數,聲明為屬性以便控制器里面可以用到
14         /// </summary>
15         public AdminUserTree leftMenu { get; set; }
16 
17 
18         public AdminBase(ApplicationDbContext context,
19                             IMemoryCache memoryCache, 
20                             ILogger<AdminUserTree> logger, 
21                             IAdminTreeService adminTreeService)
22         {
23             _basecontext = context;
24             //初始化無限極分類管理菜單
25             _logger = logger;
26             _adminTreeService = adminTreeService;
27             _memoryCache = memoryCache;
28             leftMenu = GetAdminTreeByCache();
29         }
30 
31         /// <summary>
32         /// 從緩存中讀取節點樹
33         /// </summary>
34         /// <param name="httpContext"></param>
35         /// <returns></returns>
36         public AdminUserTree GetAdminTreeByCache()
37         {
38             string cacheKey = "AdminTree-Base";
39             AdminUserTree adminTree;
40 
41             //獲取緩存內容的一種方式
42             // greeting = _memoryCache.Get(cacheKey) as string;
43             
44             //另一種獲取緩存內容的方式,
45            // alternately, TryGet returns true if the cache entry was found
46             if (!_memoryCache.TryGetValue(cacheKey, out adminTree))
47             {
48                 //緩存中不存在該內容,從數據接口中通過訪問數據庫獲取。
49                 adminTree = _adminTreeService.GetAllTreeData;
50 
51                 // 絕對過期時間方法,在設置緩存時3分鍾后過期
52                 //_memoryCache.Set(cacheKey, adminTree,
53                 //    new MemoryCacheEntryOptions()
54                 //    .SetAbsoluteExpiration(TimeSpan.FromMinutes(3)));
55                 //相對過期時間方法,相對於最后一次調用后3分鍾過期
56                 _memoryCache.Set(cacheKey, adminTree,
57                     new MemoryCacheEntryOptions()
58                     .SetSlidingExpiration(TimeSpan.FromMinutes(3)));
59                 //記錄日志
60                 _logger.LogInformation($"{cacheKey} From Data."+DateTime.Now.AddMinutes(3).ToString());
61             }
62             else
63             {
64                 //記錄日志,這里表示當前數據是從緩存中讀取的。
65                 _logger.LogInformation($"{cacheKey} From Cacha."+DateTime.Now.ToString());
66             }
67 
68             return adminTree;
69 
70         }
71 }
AdminBase

4.最后不要忘記了在Startup.cs中加入以下代碼 :

1 public void ConfigureServices(IServiceCollection services)
2         {
3             …………
4              services.AddMemoryCache();
5 
6              services.AddTransient<IAdminTreeService, AdminTreeServices>();
7

 

 

在這里基本就實現了通過緩存讀取數據了!至於Controller中的實現沒有變化請參考我的上篇隨筆:

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

為了方便閱讀這里再放一遍菜單節點類代碼:

 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 Text { 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     }
View Code

最后把運行時日志截圖放出來:

首次訪問:

上面有很多的數據庫訪問語句,日志提示數據來源與數據庫

再次訪問:

這里就是直接從緩存中讀取出來的內容了!

好了今天的內容就寫到這里。

按照先前的計划,下一步該研究一下用戶及權限的內容了,這次我打算研究一下 ASP.NET Identity 身份驗證和基於角色的授權,網上有很多相關的資料了,可我還是沒有看明白怎么和我實際的項目相結合。估計要花幾天時間了!

 


免責聲明!

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



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