using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Castle.Core.Logging; using Nito.AsyncEx; namespace Abp.Runtime.Caching { /// /// Base class for caches. /// It's used to simplify implementing . /// public abstract class CacheBase : ICache { public ILogger Logger { get; set; } public string Name { get; } public TimeSpan DefaultSlidingExpireTime { get; set; } public TimeSpan? DefaultAbsoluteExpireTime { get; set; } protected readonly object SyncObj = new object(); private readonly AsyncLock _asyncLock = new AsyncLock(); /// /// Constructor. /// /// protected CacheBase(string name) { Name = name; DefaultSlidingExpireTime = TimeSpan.FromHours(1); Logger = NullLogger.Instance; } public virtual object Get(string key, Func factory) { object item = null; try { item = GetOrDefault(key); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } if (item == null) { lock (SyncObj) { try { item = GetOrDefault(key); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } if (item == null) { item = factory(key); if (item == null) { return null; } try { Set(key, item); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } } } } return item; } public virtual object[] Get(string[] keys, Func factory) { object[] items = null; try { items = GetOrDefault(keys); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } if (items == null) { items = new object[keys.Length]; } if (items.Any(i => i == null)) { lock (SyncObj) { try { items = GetOrDefault(keys); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } var fetched = new List>(); for (var i = 0; i < items.Length; i++) { string key = keys[i]; object value = items[i]; if (value == null) { value = factory(key); } if (value != null) { fetched.Add(new KeyValuePair(key, value)); } } if (fetched.Any()) { try { Set(fetched.ToArray()); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } } } } return items; } public virtual async Task GetAsync(string key, Func> factory) { object item = null; try { item = await GetOrDefaultAsync(key); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } if (item == null) { using (await _asyncLock.LockAsync()) { try { item = await GetOrDefaultAsync(key); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } if (item == null) { item = await factory(key); if (item == null) { return null; } try { await SetAsync(key, item); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } } } } return item; } public virtual async Task GetAsync(string[] keys, Func> factory) { object[] items = null; try { items = await GetOrDefaultAsync(keys); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } if (items == null) { items = new object[keys.Length]; } if (items.Any(i => i == null)) { using (await _asyncLock.LockAsync()) { try { items = await GetOrDefaultAsync(keys); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } var fetched = new List>(); for (var i = 0; i < items.Length; i++) { string key = keys[i]; object value = items[i]; if (value == null) { value = factory(key); } if (value != null) { fetched.Add(new KeyValuePair(key, value)); } } if (fetched.Any()) { try { await SetAsync(fetched.ToArray()); } catch (Exception ex) { Logger.Error(ex.ToString(), ex); } } } } return items; } public abstract object GetOrDefault(string key); public virtual object[] GetOrDefault(string[] keys) { return keys.Select(GetOrDefault).ToArray(); } public virtual Task GetOrDefaultAsync(string key) { return Task.FromResult(GetOrDefault(key)); } public virtual Task GetOrDefaultAsync(string[] keys) { return Task.FromResult(GetOrDefault(keys)); } public abstract void Set(string key, object value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null); public virtual void Set(KeyValuePair[] pairs, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null) { foreach (var pair in pairs) { Set(pair.Key, pair.Value, slidingExpireTime, absoluteExpireTime); } } public virtual Task SetAsync(string key, object value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null) { Set(key, value, slidingExpireTime, absoluteExpireTime); return Task.FromResult(0); } public virtual Task SetAsync(KeyValuePair[] pairs, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null) { return Task.WhenAll(pairs.Select(p => SetAsync(p.Key, p.Value, slidingExpireTime, absoluteExpireTime))); } public abstract void Remove(string key); public virtual void Remove(string[] keys) { foreach (var key in keys) { Remove(key); } } public virtual Task RemoveAsync(string key) { Remove(key); return Task.FromResult(0); } public virtual Task RemoveAsync(string[] keys) { return Task.WhenAll(keys.Select(RemoveAsync)); } public abstract void Clear(); public virtual Task ClearAsync() { Clear(); return Task.FromResult(0); } public virtual void Dispose() { } } }