csharp/1100100/Dapper.Extensions/src/Dapper.Extensions/Caching/DefaultCacheKeyBuilder.cs

DefaultCacheKeyBuilder.cs
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Security.Cryptography;

namespace Dapper.Extensions.Caching
{
    public clast DefaultCacheKeyBuilder : ICacheKeyBuilder, IDisposable
    {
        private static readonly ConcurrentDictionary ParamProperties = new ConcurrentDictionary();

        private static readonly char[] Digitals = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

        private CacheConfiguration CacheConfiguration { get; }

        [ThreadStatic] private static MD5 _md5Hasher;

        public DefaultCacheKeyBuilder(CacheConfiguration configuration)
        {
            CacheConfiguration = configuration;
        }
        public string Generate(string sql, object param, string customKey, int? pageIndex = default, int? pageSize = default)
        {
            if (!string.IsNullOrWhiteSpace(customKey))
                return $"{CacheConfiguration.KeyPrefix}{(string.IsNullOrWhiteSpace(CacheConfiguration.KeyPrefix) ? "" : ":")}{customKey}";

            if (string.IsNullOrWhiteSpace(sql))
                throw new ArgumentNullException(nameof(sql));

            var builder = new StringBuilder();
            builder.AppendFormat("{0}:", sql);

            if (param == null)
                return $"{CacheConfiguration.KeyPrefix}{(string.IsNullOrWhiteSpace(CacheConfiguration.KeyPrefix) ? "" : ":")}{HashCacheKey(builder.ToString().TrimEnd(':'))}";

            var prop = GetProperties(param);
            foreach (var item in prop)
            {
                builder.AppendFormat("{0}={1}&", item.Name, item.GetValue(param));
            }
            if (pageIndex.HasValue)
            {
                builder.AppendFormat("pageindex={0}&", pageIndex.Value);
            }
            if (pageSize.HasValue)
            {
                builder.AppendFormat("pagesize={0}&", pageSize.Value);
            }
            return $"{CacheConfiguration.KeyPrefix}{(string.IsNullOrWhiteSpace(CacheConfiguration.KeyPrefix) ? "" : ":")}{HashCacheKey(builder.ToString().TrimEnd('&'))}";
        }

        private static IEnumerable GetProperties(object param)
        {
            return ParamProperties.GetOrAdd(param.GetType(), key =>
            {
                return key.GetProperties().Where(p => p.CanRead).ToList();
            });
        }

        private static string HashCacheKey(string source)
        {
            if (string.IsNullOrWhiteSpace(source))
                return string.Empty;

            _md5Hasher ??= MD5.Create();
            var bytes = Encoding.UTF8.GetBytes(source);
            var hash = _md5Hasher.ComputeHash(bytes);
            return ToString(hash);
        }

        private static string ToString(byte[] bytes)
        {
            const int byteLen = 2;
            var chars = new char[byteLen * bytes.Length];
            var index = 0;
            foreach (var item in bytes)
            {
                chars[index] = Digitals[item >> 4/* byte high */]; ++index;
                chars[index] = Digitals[item & 15/* byte low  */]; ++index;
            }
            return new string(chars);
        }

        public void Dispose()
        {
            _md5Hasher?.Dispose();
        }
    }
}