using System.Collections.Immutable; using System.Globalization; using Connected.Interop; using Connected.Threading; namespace Connected.Caching; public abstract class StatefulCacheClient : CacheClient, IStatefulCacheClient where TEntry : class { public event CacheInvalidateHandler Invalidate; protected StatefulCacheClient(ICachingService cachingService, string key) : base(cachingService, key) { Locker = new AsyncLockerSlim(); CachingService.Invalidating += OnInvalidate; } private AsyncLockerSlim? Locker { get; set; } protected virtual InvalidateBehavior InvalidateBehavior { get; } = InvalidateBehavior.KeepSameInstance; private bool Initialized { get; set; } private async void OnInvalidate(CacheEventArgs e) { if (string.Equals(e.Key, Key, StringComparison.OrdinalIgnoreCase)) { if (Initialized) await OnInvalidate(TypeConversion.Convert(e.Id, CultureInfo.InvariantCulture)); Invalidate?.Invoke(e); e.Behavior = InvalidateBehavior; } } protected virtual async Task OnInvalidate(TKey id) { await Task.CompletedTask; } protected virtual async Task OnInitializing() { await Task.CompletedTask; } protected async Task Initialize() { if (Initialized || IsDisposed || Locker is null) return; await Locker.LockAsync(async () => { if (Initialized || IsDisposed) return; await OnInitializing(); Initialized = true; }); if (Initialized) await OnInitialized(); } protected virtual async Task OnInitialized() { await Task.CompletedTask; } protected override async Task?> All() { await Initialize(); return base.All().Result; } protected override async Task First() { await Initialize(); return await base.First(); } protected override async Task Get(Func predicate) { await Initialize(); return await base.Get(predicate); } protected override async Task Get(TKey id) { await Initialize(); return await base.Get(id); } protected override async Task Get(TKey id, Func> retrieve) { await Initialize(); return await base.Get(id, retrieve); } protected override async Task?> Where(Func predicate) { await Initialize(); return await base.Where(predicate); } protected override void OnDisposing() { if (Locker is not null) { Locker?.Dispose(); Locker = null; } } public override IEnumerator GetEnumerator() { AsyncUtils.RunSync(Initialize); return base.GetEnumerator(); } }