/// <summary> /// Attempt to return an object from the cache. If the item doesn't /// exist or returns an error, call a Func to return the latest /// version of an object and insert the result in the cache. /// /// For most Internet applications, this method is the best method to /// call to fetch static data (i.e. images) from the network. /// </summary> /// <param name="blobCache">The cache to get the item.</param> /// <param name="key">The key to associate with the object.</param> /// <param name="fetchFunc">A Func which will asynchronously return /// the latest value for the object should the cache not contain the /// key. /// /// Observable.Start is the most straightforward way (though not the /// most efficient!) to implement this Func.</param> /// <param name="absoluteExpiration">An optional expiration date.</param> /// <typeparam name="T">The type of item to get.</typeparam> /// <returns>A Future result representing the deserialized object from /// the cache.</returns> public static IObservable <T> GetOrFetchObject <T>(this IBlobCache blobCache, string key, Func <IObservable <T> > fetchFunc, DateTimeOffset?absoluteExpiration = null) { return(blobCache.GetObject <T>(key).Catch <T, Exception>(_ => { var prefixedKey = blobCache.GetHashCode().ToString(CultureInfo.InvariantCulture) + key; var result = Observable.Defer(fetchFunc) .Do(x => blobCache.InsertObject(key, x, absoluteExpiration)) .Finally(() => _inflightFetchRequests.TryRemove(prefixedKey, out var _)) .Multicast(new AsyncSubject <T>()).RefCount(); return (IObservable <T>)_inflightFetchRequests.GetOrAdd(prefixedKey, result); })); }
/// <summary> /// Attempt to return an object from the cache. If the item doesn't /// exist or returns an error, call a Func to return the latest /// version of an object and insert the result in the cache. /// /// For most Internet applications, this method is the best method to /// call to fetch static data (i.e. images) from the network. /// </summary> /// <param name="key">The key to associate with the object.</param> /// <param name="fetchFunc">A Func which will asynchronously return /// the latest value for the object should the cache not contain the /// key. /// /// Observable.Start is the most straightforward way (though not the /// most efficient!) to implement this Func.</param> /// <param name="absoluteExpiration">An optional expiration date.</param> /// <returns>A Future result representing the deserialized object from /// the cache.</returns> public static IObservable <T> GetOrFetchObject <T>(this IBlobCache This, string key, Func <IObservable <T> > fetchFunc, DateTimeOffset?absoluteExpiration = null) { return(This.GetObject <T>(key).Catch <T, Exception>(_ => { object dontcare; var prefixedKey = This.GetHashCode().ToString() + key; var result = Observable.Defer(() => fetchFunc()) .Do(x => This.InsertObject(key, x, absoluteExpiration)) .Finally(() => inflightFetchRequests.TryRemove(prefixedKey, out dontcare)) .Multicast(new AsyncSubject <T>()).RefCount(); return (IObservable <T>)inflightFetchRequests.GetOrAdd(prefixedKey, result); })); }