前言
上一篇中,我們用了反射工廠來解除BLL和UI層耦合的問題。當然那是最簡單的解決方法,再複雜一點的程式可能思路相同,但是在程式設計細節中需要考慮的就更多了,比如今天我在重構過程中遇到的問題。也是接下來我要解決的問題,快取模組。為什麼要解決這個問題呢,由於我們有些下載程式碼執行的小夥伴,發現怎麼執行報錯,原來是沒有裝redis。可是我只想看layim和signalr程式碼而已啊,不想裝什麼redis。那麼基於昨天的經驗,我把快取模組同樣提取出介面,然後加了一個原始的cache層。這個cache是基於System.Web.Caching.Cache來實現的。
實現思路
正如前言中所說,實現思路還是利用反射工廠,讀取使用者的配置來反射動態生成物件。Cache程式碼結構調整如下:
首先說明一下,由於介面內部方法目前只是根據專案需要來設計,可能不全面或者不夠靈活,不過沒關係,後期可以完善。目前介面(ICache)中包含如下方法:
public interface ICache { /// <summary> /// 獲取快取,根據key /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns>返回獲取到的值</returns> T Get<T>(string key); /// <summary> /// 增加快取 /// </summary> /// <typeparam name="T">T</typeparam> /// <param name="key">key</param> /// <param name="value">value</param> /// <returns>新增成功返回true,否則返回false</returns> bool Set<T>(string key, T value); bool Set<T>(string key, T value, DateTimeOffset offset); /// <summary> /// 判斷key是否存在 /// </summary> /// <param name="key">key</param> /// <returns>存在返回true,否則返回false</returns> bool Exists(string key); /// <summary> /// 刪除快取 /// </summary> /// <param name="key"></param> /// <returns>返回是否刪除成功,true或者false</returns> bool Delete(string key); /// <summary> /// 獲取雜湊表中的值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="hashKey"></param> /// <param name="key"></param> /// <returns></returns> T HashGet<T>(string hashKey, string key); /// <summary> /// 設定雜湊快取值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="hashKey">hashKey</param> /// <param name="key">key</param> /// <param name="value">value</param> /// <returns>返回是否設定成功</returns> bool HashSet<T>(string hashKey, string key, T value); /// <summary> /// 刪除雜湊快取中的某個key /// </summary> /// <param name="hashKey"></param> /// <param name="key"></param> /// <returns>返回是否刪除成功</returns> bool HashDelete(string hashKey, string key); }
很簡單的幾個方法,相信複雜的專案或者需求肯定要有更多的功能。這裡姑且不討論。然後我們按照上圖中新建資料夾,分別對應我們要切換的Cache型別。比如,我新建了,Memcached,Redis,Memory三個資料夾,前兩個不用說,第三個就是我們使用框架自帶的Cache。下面的程式碼是我參照網路上的一段文章又簡單包裝了一下寫的。這裡寫這個Cache只是為了後續提到的,我們能夠自由切換而已。能用第三方的快取還是用第三方的。程式碼如下:(實現介面中的方法)
public class Cache : ICache { private static System.Web.Caching.Cache _cache; const int defaultTime = 24 * 60;//一天 public Cache() { //初始化 _cache = HostingEnvironment.Cache; SaveTime = defaultTime; } public static double SaveTime { get; set; } public bool Delete(string key) { var obj =_cache.Remove(key); return obj != null; } public bool Exists(string key) { return true; } public T Get<T>(string key) { if (string.IsNullOrEmpty(key)) { return default(T); } return (T)_cache.Get(key); } public bool HashDelete(string hashKey, string key) { Hashtable table = Get<Hashtable>(hashKey); if (table == null) { return true; } else { if (table.ContainsKey(key)) { table.Remove(key); } } return true; } public T HashGet<T>(string hashKey, string key) { Hashtable table = Get<Hashtable>(hashKey); if (table != null) { var value = table[key]; if (value == null) { return default(T); } return (T)value; } return default(T); } public bool HashSet<T>(string hashKey, string key, T value) { //這裡就是用hashtable做雜湊儲存 Hashtable table = Get<Hashtable>(hashKey); if (table == null) { table = new Hashtable(); table.Add(key, value); } else { if (table.ContainsKey(key)) { table[key] = value; } else { table.Add(key, value); } } return Set(hashKey, table); } #region private void Insert(string key, object value, CacheDependency dependency, CacheItemPriority priority, CacheItemRemovedCallback callback) { _cache.Insert(key, value, dependency,System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(SaveTime), priority, callback); } private void Insert(string key, object value, CacheDependency dependency, CacheItemRemovedCallback callback) { Insert(key, value, dependency, CacheItemPriority.Default, callback); } private void Insert(string key, object value, CacheDependency dependency) { Insert(key, value, dependency, CacheItemPriority.Default, null); } private void Insert(string key, object value) { Insert(key, value, null, CacheItemPriority.Default, null); } #endregion public bool Set<T>(string key, T value) { Insert(key, value); return true; } public bool Set<T>(string key, T value, DateTimeOffset offset) { SaveTime = offset.Offset.Minutes; Insert(key, value); SaveTime = defaultTime; return true; } }
這樣的話,我們同樣用上一篇文章中寫的方法來生成對用的ICache物件。
public class LayIMCacheFactory : LayIMFactory { public LayIMCacheFactory() { asemmblyPath = "LayIM.Cache.Classes.{0}.{1},LayIM.Cache"; _type = "CacheType"; } public ICache CreateCache() { return Create<ICache>(FactoryClasses.Cache); } public LayIMCache CreateLayIMCache() { ICache cache = CreateCache();
return new LayIMCache(cache); } }
可以看到,上述程式碼中,CreateLayIMCache方法中返回的是LayIMCache,是因為LayIMCache相當於業務層了,雖然也做了介面,但是(暫時)沒有必要再區分了,因為,在建構函式中,我把ICache的例項傳給LayIMCache中,然後內部呼叫相應的Cache方法,然後再最外部呼叫LayIMCache。可能把大家繞暈了。畫個圖更形象一些.(我也不會UML圖,實在慚愧,將就看吧~)
現在我們來演示一下。怎麼能看出不同呢,由於Redis使能夠將我們存的快取資料持久化的。而Memory的這個Cache只要程式停了,他就消失了。那麼,我們可以通過驗證登入token的方法來測試。首先Redis不必說。我們更改web.config中的CacheType值。
<!--快取型別,Redis Memory,Memcached--> <add key="CacheType" value="Memory" />
這裡要注意,不論是Memory還是Redis或者其他,這裡的配置字母一定要寫正確,否則反射生成例項的時候會報錯。
我們執行一下,打斷點除錯:
這時候我們在關閉程式,重新執行除錯,我們走到token驗證,看一下內容,已經沒有了,如下圖,驗證已經進入非授權條件內。
總結
本篇已經接近尾聲了,原來寫程式碼寫多了,頭腦真的會升華。以前看設計模式中的程式碼壓根體會不到其中的奧妙,如今專門做一下程式碼重構工作才能真正體驗到程式碼設計的精妙之處。騷年還需要努力啊。今天的程式碼重構工作就到此結束,不想裝Redis的同學趕緊試試用這個方法切換快取吧。不知道我在說些什麼的同學可以移步這裡哦:
ASP.NET SignalR 與 LayIM2.0 配合輕鬆實現Web聊天室 實戰系列(不斷更新中)