利用委托实现自己的数据缓存仓库(附上Demo)

Demo源码

写在前面的话

  写完这篇博客后,总觉得少了些什么,后来想了下,感觉自己只是把结果给亮了出来,自己为什么想到这么做,这个类库出生的缘由未详述,因此,在本段作下说明,如有不足之处,希望能和大家一起交流沟通。。,大家共同提高啊!

  我的想法很简单,我在拥有:

  1. 获取数据的方法

  并面对了下面的场景:

  1. 数据仅从数据库读取,不在程序中被修改
  2. 获取数据的方法在程序的很多地方被频繁的调用
  3. 我能把控数据的准确性对程序的影响

  我希望:可以有一个容器,让我在程序的入口处进行数据的初始化(丢给它获取数据的方法、key、过期时间)后,我能够在程序中其它任何地方通过key来获取到数据,并且,数据能够根据我设定的过期时间进行有效地更新!

  以此来达到:

  1. 缓存直接和方法进行绑定,而并非直接将具体值丢入缓存
  2. 避免在不同的页面声明相同的静态变量来获取相同的数据,减少代码量
  3. 统一在程序的入口管理所有的全局变量,提高代码的可维护性
  4. 避免频繁读取数据库,提高应用程序的性能

背景

  本来想聊聊本文产生的背景的,后来发现本码农词穷了。因此,直入主题,本文的工作是利用委托实现了一个全局的数据缓存仓库。

  这个类库接收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分钟,结果很显而易见。

  

原文地址:https://www.cnblogs.com/SmallZL/p/4007128.html