using Microsoft.Extensions.Caching.Memory; using Kit.Helpers; namespace Kit.Helpers.Cache { public abstract class BaseCacheProvider : ICacheProvider { private readonly Dictionary _lockObjects = new Dictionary(1000); private readonly object _keysLock = new object(); public abstract bool Contains(string cacheName); public abstract void PurgeCacheItems(string cacheName, bool isPrefix = false); public abstract void SetCacheData(string cacheName, TObject data, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null); public abstract TResult GetCacheData(string cacheName); protected string CacheNameNormalize(string casheName) { if (casheName == null) return string.Empty; return casheName.ToLower(); } protected object GetLockObject(string cacheName) { lock (_keysLock) { if (!this._lockObjects.ContainsKey(cacheName)) this._lockObjects.Add(cacheName, new object()); return this._lockObjects[cacheName]; } } public virtual string CreateCacheName(object prefix, params object[] args) { prefix = prefix != null ? prefix.ToString()!.AddSufixIsNotEmpty("_") : string.Empty; string end = string.Empty; if (!args.IsNullOrEmpty()) { end = args.Where(x => x != null).Select(x => x.ToString()).Join("_"); } return $"{prefix}{end}"; } public virtual TResult GetCacheData(string cacheName, Func getMethod, MemoryCacheEntryOptions setProperties = null) { cacheName = CacheNameNormalize(cacheName); Check.ArgumentIsNotNull(getMethod, "getMethod"); if (cacheName.IsNullOrEmpty()) return (TResult)getMethod(); // если есть данные в кеше но возвращаем TResult data = GetCacheData(cacheName); if (data != null) return data; lock (this.GetLockObject(cacheName)) { // повторная проверка после блокировки data = this.GetCacheData(cacheName); if (data != null) return data; // если в кеше данных нет то выполняем метод data = getMethod(); this.SetCacheData(cacheName, data, setProperties); } return data; } public TResult GetCacheData(string cacheName, Func getMethod, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null) { cacheName = CacheNameNormalize(cacheName); Check.ArgumentIsNotNull(getMethod, "getMethod"); if (cacheName.IsNullOrEmpty()) return (TResult)getMethod(); // если есть данные в кеше но возвращаем TResult data = GetCacheData(cacheName); if (data != null) return data; lock (this.GetLockObject(cacheName)) { // повторная проверка после блокировки data = this.GetCacheData(cacheName); if (data != null) return data; // если в кеше данных нет то выполняем метод data = getMethod(); this.SetCacheData(cacheName, data, setProperties, callback); } return data; } } }