static IObservable <T> GetAndFetchLatestFromIndex <T>(this IBlobCache This, string key, Func <IObservable <T> > fetchFunc, Action <T> removedItemsCallback, Func <DateTimeOffset, bool> fetchPredicate = null, DateTimeOffset?absoluteExpiration = null, bool shouldInvalidateOnError = false) where T : CacheItem { var idx = Observable.Defer(() => This .GetOrCreateObject(key, () => CacheIndex.Create(key))) .Select(x => x.IndexKey == null ? CacheIndex.Create(key) : x) .Replay() .RefCount(); var fetch = idx .Select(x => Tuple.Create(x, fetchPredicate == null || !x.Keys.Any() || fetchPredicate(x.UpdatedAt))) .Where(predicateIsTrue => predicateIsTrue.Item2) .Select(x => x.Item1) .Select(index => index.Clear()) .SelectMany(index => fetchFunc() .Catch <T, Exception>(ex => { var shouldInvalidate = shouldInvalidateOnError ? This.InvalidateObject <CacheIndex>(key) : Observable.Return(Unit.Default); return(shouldInvalidate.SelectMany(__ => Observable.Throw <T>(ex))); }) .SelectMany(x => x.Save <T>(This, key, absoluteExpiration)) .Do(x => index.Add(key, x)) ); var cache = idx .SelectMany(index => This.GetObjects <T>(index.Keys.ToList())) .SelectMany(dict => dict.Values); return(cache.Merge(fetch) .Finally(async() => { var index = await idx; await index.Save(This); var list = index.OldKeys.Except(index.Keys); if (!list.Any()) { return; } var removed = await This.GetObjects <T>(list); foreach (var d in removed.Values) { removedItemsCallback(d); } await This.InvalidateObjects <T>(list); }) .Replay().RefCount()); }
/// <summary> /// This method adds a new object to the database and updates the /// corresponding index. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="blobCache">The cache to retrieve the object from.</param> /// <param name="key">The key to look up the cache value with.</param> /// <param name="item">The item to add to the database</param> /// <param name="maxCacheDuration"> /// The maximum age of a cache object before the object is treated as /// expired and unusable. Cache objects older than this will be treated /// as a cache miss. /// <returns></returns> public static IObservable <T> PutAndUpdateIndex <T>(this IBlobCache blobCache, string key, Func <IObservable <T> > fetchFunc, TimeSpan maxCacheDuration) where T : CacheItem { return(Observable.Defer(() => { var absoluteExpiration = blobCache.Scheduler.Now + maxCacheDuration; return blobCache.GetOrCreateObject(key, () => CacheIndex.Create(key)) .SelectMany(index => fetchFunc() .Catch <T, Exception>(Observable.Throw <T>) .SelectMany(x => x.Save <T>(blobCache, key, absoluteExpiration)) .Do(x => index.AddAndSave(blobCache, key, x, absoluteExpiration)) ); })); }
static IObservable <T> GetAndFetchLatestFromIndex <T>(this IBlobCache This, string key, Func <IObservable <T> > fetchFunc, Action <T> removedItemsCallback, Func <DateTimeOffset, bool> fetchPredicate = null, DateTimeOffset?absoluteExpiration = null, bool shouldInvalidateOnError = false) where T : CacheItem { var fetch = Observable.Defer(() => This.GetOrCreateObject(key, () => CacheIndex.Create(key)) .Select(x => Tuple.Create(x, fetchPredicate == null || !x.Keys.Any() || fetchPredicate(x.UpdatedAt))) .Where(predicateIsTrue => predicateIsTrue.Item2) .Select(x => x.Item1) .SelectMany(index => index.Clear(This, key, absoluteExpiration)) .SelectMany(index => { var fetchObs = fetchFunc().Catch <T, Exception>(ex => { var shouldInvalidate = shouldInvalidateOnError ? This.InvalidateObject <CacheIndex>(key) : Observable.Return(Unit.Default); return(shouldInvalidate.SelectMany(__ => Observable.Throw <T>(ex))); }); return(fetchObs .SelectMany(x => x.Save <T>(This, key, absoluteExpiration)) .Do(x => index.AddAndSave(This, key, x, absoluteExpiration)) .Finally(() => { This.GetObjects <T>(index.OldKeys.Except(index.Keys)) .Do(dict => This.InvalidateObjects <T>(dict.Keys)) .SelectMany(dict => dict.Values) .Do(removedItemsCallback) .Subscribe(); })); })); var cache = Observable.Defer(() => This.GetOrCreateObject(key, () => CacheIndex.Create(key)) .SelectMany(index => This.GetObjects <T>(index.Keys)) .SelectMany(dict => dict.Values)); return(cache.Merge(fetch).Replay().RefCount()); }