天天看點

利用委托實作自己的資料緩存倉庫(附上Demo)

我的想法很簡單,我在擁有:

擷取資料的方法

  并面對了下面的場景:

資料僅從資料庫讀取,不在程式中被修改

擷取資料的方法在程式的很多地方被頻繁的調用

我能把控資料的準确性對程式的影響

  我希望:可以有一個容器,讓我在程式的入口處進行資料的初始化(丢給它擷取資料的方法、key、過期時間)後,我能夠在程式中其它任何地方通過key來擷取到資料,并且,資料能夠根據我設定的過期時間進行有效地更新!

  以此來達到:

緩存直接和方法進行綁定,而并非直接将具體值丢入緩存

避免在不同的頁面聲明相同的靜态變量來擷取相同的資料,減少代碼量

統一在程式的入口管理所有的全局變量,提高代碼的可維護性

避免頻繁讀取資料庫,提高應用程式的性能

Demo源碼

寫在前面的話

  寫完這篇部落格後,總覺得少了些什麼,後來想了下,感覺自己隻是把結果給亮了出來,自己為什麼想到這麼做,這個類庫出生的緣由未詳述,是以,在本段作下說明,如有不足之處,希望能和大家一起交流溝通。。,大家共同提高啊!

  我的想法很簡單,我在擁有:

背景

  本來想聊聊本文産生的背景的,後來發現本碼農詞窮了。是以,直入主題,本文的工作是利用委托實作了一個全局的資料緩存倉庫。

  這個類庫接收4個參數:1 您要存儲的資料的資料類型 2 擷取需要存儲資料的方法 3 過期的時間 4 讀取該資料的唯一key

  這個類庫就能夠:根據key擷取資料,資料通過執行您傳入的擷取資料的方法來獲得,當上一次擷取的時間過期後,會重新執行擷取資料的方法以更新資料!

  推薦的使用方式:1 在應用程式的開端,進行所有需要緩存資料的初始化操作,統一管理所有的key;這樣可以避免不必要的混亂

          2 在應用程式中需要使用的地方,直接通過key進行資料的擷取,這樣避免了在每個頁面中寫重複的代碼,提高應用的效率

實作

  第一步,我們為需要存儲的資料定義一個标準的資料結構:

  

/// <summary>
    /// 存儲的資料結構
    /// </summary>
    /// <typeparam name="T">需要存儲的資料的資料類型(string int ..)</typeparam>
    public sealed class StoredDataInfo<T>
    {

        /// <summary>
        /// 存儲的資料
        /// </summary>
        public T Data { get; set; }

        /// <summary>
        /// 擷取需要存儲的資料的方法
        /// </summary>
        public Func<T> GetDataMethod { get; set; }

        /// <summary>
        /// 資料過期的時間
        /// </summary>
        public int TimeOfDuration { get; set; }

        /// <summary>
        /// 資料上一次被更新的時間
        /// </summary>
        public DateTime LastModifyTime { get; set; }

    }      

  以上代碼一目了然,不用多說,大家都懂得。

  第二步,我們需要一個清單來存儲需要的資料,因為我們會存儲很多的資料

//存儲所有資料
        /// <summary>
        /// 存儲所有資料
        /// </summary>
        private static readonly Dictionary<string, StoredDataInfo<T>> EntireStoredData = new Dictionary<string, StoredDataInfo<T>>();      

  第三步,一個初始化資料的方法

//初始化資料項
        /// <summary>
        /// 初始化資料項
        /// </summary>
        /// <param name="key"></param>
        /// <param name="storedData"></param>
        private static string InitStoredDataItem(string key, StoredDataInfo<T> storedData)
        {
            lock (lockObj)
            {
                if (EntireStoredData.ContainsKey(key))
                {
                    return "key:" + key + " 已存在";
                }
                EntireStoredData.Add(key, storedData);
            }
            return "";
        }      

  第四步,根據key擷取資料項的方法

// 擷取指定key的資料項
        /// <summary>
        /// 擷取指定key的資料項
        /// </summary>
        /// <param name="key"></param>
        /// <param name="isForcedRefresh">是否強制更新</param>
        /// <returns></returns>
        public static T GetData(string key, bool isForcedRefresh = false)
        {
            //不存在key
            if (!HasKey(key))
            {
                #region

                string currKeys = "";
                string currTType = "";
                if (EntireStoredData.Any())
                {
                    currKeys = string.Join(",", EntireStoredData.Keys.ToArray());
                    var v = EntireStoredData.First().Value.Data;
                    currTType = v.GetType().ToString();
                }
                throw new Exception(string.Format("無指定key:{0},目前池包含key集合{1},目前池類型:{2}", key, currKeys, currTType));

                #endregion
            }

            //根據key擷取value
            StoredDataInfo<T> sdi = EntireStoredData[key];

            //判斷是否過期
            int timeOfDuration = sdi.TimeOfDuration;
            DateTime lastModifyTime = sdi.LastModifyTime;

            if (!isForcedRefresh && DateTime.Now.AddMinutes(-timeOfDuration) <= lastModifyTime)
                return sdi.Data;

            //重新更新資料
            sdi.Data = sdi.GetDataMethod();
            sdi.LastModifyTime = DateTime.Now;

            return sdi.Data;
        }      

使用

static void Main(string[] args)
        {
            #region 資料緩存倉庫測試

            //key
            const string key = "GetCurrDateKey";
            //初始化倉庫
            DataWarehouse<string>.InitDataItem(key, GetCurrDate, 1);

            //根據key擷取值
            Console.WriteLine(DataWarehouse<string>.GetData(key));
            //休眠 等待過期
            Thread.Sleep(1000 * 61);
            //再次根據key擷取值
            Console.WriteLine(DataWarehouse<string>.GetData(key));

            Console.ReadLine();

            #endregion
        }

        /// <summary>
        /// 擷取時間
        /// </summary>
        /// <returns></returns>
        private static string GetCurrDate()
        {
            return DateTime.Now.ToString();
        }      

  以上,做了一個很小的測試,存儲一個目前時間的string類型的值,設定過期時間為1分鐘,結果很顯而易見。

利用委托實作自己的資料緩存倉庫(附上Demo)