ASP.NET SignalR 與 LayIM2.0 配合輕松實現Web聊天室(十二) 代碼重構使用反射工廠解耦(一)緩存切換


前言

  上一篇中,我們用了反射工廠來解除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聊天室 實戰系列(不斷更新中)

 


免責聲明!

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



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