写一个用于缓存数据库查询结果的仓库

在写一个功能时,为避免从数据库中反复读取各种对象,于是写一个简单的仓库用于缓存。仓库定义如下:

    public abstract class ReadOnlyRepository<TKey, TValue> where TKey : notnull
    {        
        protected ConcurrentDictionary<TKey, TValue> Items { get; set; } = new ConcurrentDictionary<TKey, TValue>();

        public TValue this[TKey key]
        {
            //根据 TKey ,返回 TValue。
        }

        public Dictionary<TKey, TValue> GetBatch(HashSet<TKey> keys)
        {
            //根据一组 TKey ,返回一组 TValue。
        }


        public bool TryGetValue(TKey key, out TValue value)
        {
            //根据 TKey ,返回 TValue,返回 bool 值指示是否找到了值
        }


        public bool TryGetBatch(HashSet<TKey> keys, out Dictionary<TKey, TValue> keyValuePairs)
        {
            //根据一组 TKey ,返回一组 TValue,返回 bool 值指示是否所有的Tkey都找到了值
        }

        protected abstract bool TryGetValueFromDb(TKey key, [MaybeNullWhen(false)] out TValue value);

        protected abstract bool TryGetBatchFromDb(HashSet<TKey> keys, out Dictionary<TKey, TValue> keyValuePairs);
        
        protected abstract string TKeyName { get; }

        protected abstract string TValueName { get; }
    }

上述定义中,4个公共函数均会先从缓存 Item 中查询,如果不存在,再尝试查询数据库。
另外两个抽象函数
protected abstract bool TryGetValueFromDb 和 protected abstract bool TryGetBatchFromDb 用于执行具体的查询数据库和批量查询数据库的逻辑留给继承的子类去实现。
还有两个抽象属性 TKeyName 和 TValueName 用于给出 key 类型和 value 类型的友好名称,也由子类实现。
完整代码如下:

   public abstract class ReadOnlyRepository<TKey, TValue> where TKey : notnull
    {
        protected ConcurrentDictionary<TKey, TValue> Items { get; set; } = new ConcurrentDictionary<TKey, TValue>();

        public TValue this[TKey key]
        {
            get
            {
                TValue? value;
                if (!TryGetValue(key, out value))
                {
                    throw new System.NullReferenceException($"Cannot find {TValueName} by {TKeyName} \"{key}\"");
                }
                else
                {
                    return value;
                }
            }
        }

        public Dictionary<TKey, TValue> GetBatch(HashSet<TKey> keys)
        {
            bool allFound;
            Dictionary<TKey, TValue> results;
            allFound = TryGetBatch(keys, out results);
            if (!allFound)
            {
                HashSet<TKey> notFound = keys.Except(results.Keys).ToHashSet();
                if (notFound.Any())
                {
                    throw new System.NullReferenceException(
                        $"Cannot find {TValueName} by {TKeyName} \"{string.Join(';', notFound.Select(x => x.ToString()))}\"");
                }
            }
            return results;
        }

        /// <summary>
        /// If found, return true; else return false
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
        {
            bool found;
            if (Items.ContainsKey(key))
            {
                found = true;
                value = Items[key];
            }
            else
            {
                found = TryGetValueFromDb(key, out value);
                if (value != null) { Items.TryAdd(key, value); }
            }
            return found;
        }

        /// <summary>
        /// If found ALL, return true; else return false
        /// </summary>
        /// <param name="keys"></param>
        /// <param name="keyValuePairs"></param>
        /// <returns></returns>
        public bool TryGetBatch(HashSet<TKey> keys, out Dictionary<TKey, TValue> keyValuePairs)
        {
            bool allFound;
            HashSet<TKey> notBuffered = keys.Except(Items.Keys).ToHashSet();

            if (notBuffered.Count == 0)
            {
                allFound = true;
            }
            else
            {
                Dictionary<TKey, TValue> pulled;
                allFound = TryGetBatchFromDb(notBuffered, out pulled);
                foreach (var item in pulled) { Items.TryAdd(item.Key, item.Value); }
            }
            keyValuePairs = new Dictionary<TKey, TValue>();
            foreach (var key in keys.Intersect(Items.Keys)) { keyValuePairs.Add(key, Items[key]); }

            return allFound;
        }

        /// <summary>
        /// If found, return true; else return false
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected abstract bool TryGetValueFromDb(TKey key, [MaybeNullWhen(false)] out TValue value);

        /// <summary>
        /// If found ALL, return true; else return false
        /// </summary>
        /// <param name="keys"></param>
        /// <param name="keyValuePairs"></param>
        /// <returns>Dictionary that contains zero or more KVPs</returns>
        protected abstract bool TryGetBatchFromDb(HashSet<TKey> keys, out Dictionary<TKey, TValue> keyValuePairs);
        
        protected abstract string TKeyName { get; }

        protected abstract string TValueName { get; }
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值