using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; using VCommon.Caching.Internals; using VCommon.Json; namespace VCommon.Caching { public class CacheManager { private readonly RedisCacheAccess _cacheAccess; public CacheManager(string server) => _cacheAccess = CacheAccess.CreateRedisAccessInstance(server); public void KeyExpire(int db, string key, CacheExpire expire) { if (null != expire) { if (expire.To.HasValue) _cacheAccess.CacheOp(db, p => p.KeyExpire(key, expire.To.Value)); if (expire.Specified.HasValue) _cacheAccess.CacheOp(db, p => p.KeyExpire(key, expire.Specified.Value)); } } public bool KeyExist(int db, string key) => _cacheAccess.CacheOp(db, p => p.KeyExists(key)); public bool KeyDelete(int db, string key) => _cacheAccess.CacheOp(db, p => p.KeyDelete(key)); public void KeySetJson(int db, string key, object value, CacheExpire expire = null) { var json = VJsonSerializer.Serialize(value); _cacheAccess.CacheOp(db, p => p.StringSet(key, json)); KeyExpire(db, key, expire); } public T KeyGetJson(int db, string key) => VJsonSerializer.Deserialize(_cacheAccess.CacheOp(db, p => p.StringGet(key))); public CacheValue KeyFetch(int db, string key, Func missingCacheValueProvider, CacheExpire expire = null) { if (KeyExist(db, key)) { return _cacheAccess.CacheOp(db, p => p.StringGet(key)); } else { var value = missingCacheValueProvider(); _cacheAccess.CacheOp(db, p => p.StringSet(key, value)); KeyExpire(db, key, expire); return value; } } public T KeyFetchJson(int db, string key, Func missingCacheValueProvider, CacheExpire expire = null) { if (KeyExist(db, key)) { var json = (string)_cacheAccess.CacheOp(db, p => p.StringGet(key)); return VJsonSerializer.Deserialize(json); } else { var value = missingCacheValueProvider(); var json = VJsonSerializer.Serialize(value); _cacheAccess.CacheOp(db, p => p.StringSet(key, json)); KeyExpire(db, key, expire); return value; } } public void KeySet(int db, string key, CacheValue value, CacheExpire expire = null) { _cacheAccess.CacheOp(db, p => p.StringSet(key, value)); KeyExpire(db, key, expire); } public CacheValue KeyGet(int db, string key) => _cacheAccess.CacheOp(db, p => p.StringGet(key)); public bool HashFieldExist(int db, string hashKey, string hashField) => _cacheAccess.CacheOp(db, p => p.HashExists(hashKey, hashField)); public bool HashFieldDelete(int db, string hashKey, string hashField) => _cacheAccess.CacheOp(db, p => p.HashDelete(hashKey, hashField)); public long HashFieldDelete(int db, string hashKey, params string[] hashFields) { if (0 == hashFields.Length) return 0; var fields = hashFields.Select(p => (RedisValue)p).ToArray(); return _cacheAccess.CacheOp(db, p => p.HashDelete(hashKey, fields)); } public CacheValue HashFieldFetch(int db, string hashKey, string hashField, Func missingCacheValueProvider, CacheExpire expire = null) { CacheValue cv = _cacheAccess.CacheOp(db, p => p.HashGet(hashKey, hashField)); if (false == cv.IsNull) return cv; var through = missingCacheValueProvider(); if (through.IsNull) return through; _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, hashField, through)); KeyExpire(db, hashKey, expire); return through; } public T HashFieldFetchJson(int db, string hashKey, string hashField, Func missingCacheValueProvider, CacheExpire expire = null) { string json = _cacheAccess.CacheOp(db, p => p.HashGet(hashKey, hashField)); if (null != json) return VJsonSerializer.Deserialize(json); var through = missingCacheValueProvider(); var json2 = VJsonSerializer.Serialize(through); _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, hashField, json2)); KeyExpire(db, hashKey, expire); return through; } public T HashFieldGetJson(int db, string hashKey, string hashField) { var json = (string)_cacheAccess.CacheOp(db, p => p.HashGet(hashKey, hashField)); return VJsonSerializer.Deserialize(json); } public void HashFieldSetJson(int db, string hashKey, string hashField, T value, CacheExpire expire = null) { var json = VJsonSerializer.Serialize(value); _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, hashField, json)); KeyExpire(db, hashKey, expire); } public void HashFieldSetJson(int db, string hashKey, IReadOnlyDictionary entries, CacheExpire expire = null) { var hashEntrys = entries.Select(p => new HashEntry(p.Key, VJsonSerializer.Serialize(p.Value))).ToArray(); _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, hashEntrys)); KeyExpire(db, hashKey, expire); } public void HashFieldPurgeAndSet(int db, string hashKey, IReadOnlyDictionary hashFields) { var entries = hashFields.Select(p => new HashEntry(p.Key, p.Value)).ToArray(); if (0 == entries.Length) return; _cacheAccess.CacheOp(db, p => { var batch = p.CreateBatch(); batch.KeyDeleteAsync(hashKey); batch.HashSetAsync(hashKey, entries); batch.Execute(); }); } public void HashFieldDeleteByPattern(int db, string hashKey, string pattern) { var entries = _cacheAccess.CacheOp(db, p => p.HashScan(hashKey, pattern, int.MaxValue)).Select(p => p.Name).ToArray(); _cacheAccess.CacheOp(db, p => p.HashDelete(hashKey, entries)); } public IReadOnlyDictionary HashFieldGetByPattern(int db, string hashKey, string pattern) { return _cacheAccess.CacheOp(db, p => p.HashScan(hashKey, pattern, int.MaxValue)) .ToDictionary(p => (string)p.Name, p => (CacheValue)p.Value); } public IReadOnlyDictionary HashFieldGetByPatternJson(int db, string hashKey, string pattern) { return _cacheAccess.CacheOp(db, p => p.HashScan(hashKey, pattern, int.MaxValue)) .ToDictionary(p => (string)p.Name, p => VJsonSerializer.Deserialize(p.Value)); } public void HashFieldSet(int db, string hashKey, string hashField, CacheValue value, CacheExpire expire = null) { _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, hashField, value)); KeyExpire(db, hashKey, expire); } public CacheValue HashFieldGet(int db, string hashKey, string hashField) => _cacheAccess.CacheOp(db, p => p.HashGet(hashKey, hashField)); public void HashFieldSet(int db, string hashKey, IReadOnlyDictionary hashField, CacheExpire expire = null) { var entries = hashField.Select(p => new HashEntry(p.Key, p.Value)).ToArray(); if (0 == entries.Length) return; _cacheAccess.CacheOp(db, p => p.HashSet(hashKey, entries)); KeyExpire(db, hashKey, expire); } public long HashFieldIncrement(int db, string hashKey, string hashField) => _cacheAccess.CacheOp(db, p => p.HashIncrement(hashKey, hashField)); public long HashFieldDecrement(int db, string hashKey, string hashField) => _cacheAccess.CacheOp(db, p => p.HashDecrement(hashKey, hashField)); public IReadOnlyDictionary HashFieldGet(int db, string hashKey, params string[] hashFields) { if (0 == hashFields.Length) return new Dictionary(); var keys = hashFields.Distinct().ToArray(); var fields = keys.Select(p => (RedisValue)p).ToArray(); return _cacheAccess.CacheOp(db, p => p.HashGet(hashKey, fields)) .Select((p, i) => new { k = keys[i], v = p }) .ToDictionary(p => p.k, p => (CacheValue)p.v); } public IReadOnlyDictionary HashFieldsFetchAsDic(int db, string key, IEnumerable hashFields, Func convertHashField, Func, IReadOnlyDictionary> missingCacheValueProvider, Func convertValueToCache, Func convertCacheToValue, CacheExpire expire = null) { var result = new Dictionary(); var missingFields = new List(); //get from cache,and collect missing { var fields = hashFields.Distinct().ToArray(); if (0 == fields.Length) return result; var hf = fields.Select(p => (RedisValue)convertHashField(p)).ToArray(); var fromCache = _cacheAccess.CacheOp(db, p => p.HashGet(key, hf)); for (var i = 0; i < fromCache.Length; i++) { if (false == fromCache[i].HasValue) missingFields.Add(fields[i]); else result[fields[i]] = convertCacheToValue(fromCache[i]); } } if (0 != missingFields.Count) //get from db,and put cache { var fromDb = missingCacheValueProvider(missingFields); var entries = fromDb.Select(p => new HashEntry(convertHashField(p.Key), convertValueToCache(p.Value))) .Where(p => false == p.Value.IsNull) .ToArray(); _cacheAccess.CacheOp(db, p => p.HashSet(key, entries)); //merge to result foreach (var kvp in fromDb) result[kvp.Key] = kvp.Value; } KeyExpire(db, key, expire); return result; } public string[] SearchKeys(int db, string pattern) => _cacheAccess.SearchKeys(db, pattern).Select(p => (string)p).ToArray(); } }