Looking at Guava Cache we think its API is more convenient than .NET's Cache API.
Just consider:
.NET has getters, and setters of objects by string keys. You should provide caching policy with each setter.
object
string
Guava cache operates with typed storage of Key to Value. Provides a value factory and a caching policy in advance at cache construction.
Key
Value.
Guava's advantange is based on an idea that homogenous storage assumes a uniform way of creation of values, and uniform caching policy. Thus a great part of logic is factored out into a cache initialization.
We have decided to create a simple adapter of the MemoryCache to achieve the same goal. Here is a result of such an experiment:
public class Cache<K, V> where V: class { /// <summary> /// A cache builder. /// </summary> public struct Builder { /// <summary> /// A memory cache. If not specified then MemoryCache.Default is used. /// </summary> public MemoryCache MemoryCache; /// <summary> /// An expiration value. /// Alternatively CachePolicyFunc can be used. /// </summary> public TimeSpan Expiration; /// <summary> /// Indicates whether to use sliding (true), or absolute (false) /// expiration. /// Alternatively CachePolicyFunc can be used. /// </summary> public bool Sliding; /// <summary> /// Optional function to get caching policy. /// Alternatively Expiration and Sliding property can be used. /// </summary> public Func<V, CacheItemPolicy> CachePolicyFunc; /// <summary> /// Optional value validator. /// </summary> public Func<V, bool> Validator; /// <summary> /// A value factory. /// Alternatively FactoryAsync can be used. /// </summary> public Func<K, V> Factory; /// <summary> /// Async value factory. /// Alternatively Factory can be used. /// </summary> public Func<K, Task<V>> FactoryAsync; /// <summary> /// A key to string converter. /// </summary> public Func<K, string> KeyFunc; /// <summary> /// Converts builder to a Cache<K, V> instance. /// </summary> /// <param name="builder">A builder to convert.</param> /// <returns>A Cache<K, V> instance.</returns> public static implicit operator Cache<K, V>(Builder builder) { return new Cache<K, V>(builder); } } /// <summary> /// Creates a cache from a cache builder. /// </summary> /// <param name="builder">A cache builder instance.</param> public Cache(Builder builder) { if ((builder.Factory == null) && (builder.FactoryAsync == null)) { throw new ArgumentException("builder.Factory"); } if (builder.MemoryCache == null) { builder.MemoryCache = MemoryCache.Default; } this.builder = builder; } /// <summary> /// Cached value by key. /// </summary> /// <param name="key">A key.</param> /// <returns>A cached value.</returns> public V this[K key] { get { return Get(key); } set { Set(key, value); } } /// <summary> /// Sets a value for a key. /// </summary> /// <param name="key">A key to set.</param> /// <param name="value">A value to set.</param> public void Set(K key, V value) { SetImpl(GetKey(key), IsValid(value) ? value : null); } /// <summary> /// Gets a value for a key. /// </summary> /// <param name="key">A key to get value for.</param> /// <returns>A value instance.</returns> public V Get(K key) { var keyValue = GetKey(key); var value = builder.MemoryCache.Get(keyValue) as V; if (!IsValid(value)) { value = CreateValue(key); SetImpl(keyValue, value); } return value; } /// <summary> /// Gets a task to return an async value. /// </summary> /// <param name="key">A key.</param> /// <returns>A cached value.</returns> public async Task<V> GetAsync(K key) { var keyValue = GetKey(key); var value = builder.MemoryCache.Get(keyValue) as V; if (!IsValid(value)) { value = await CreateValueAsync(key); SetImpl(keyValue, value); } return value; } /// <summary> /// Gets string key value for a key. /// </summary> /// <param name="key">A key.</param> /// <returns>A string key value.</returns> protected string GetKey(K key) { return builder.KeyFunc != null ? builder.KeyFunc(key) : key == null ? null : key.ToString(); } /// <summary> /// Creates a value for a key. /// </summary> /// <param name="key">A key to create value for.</param> /// <returns>A value instance.</returns> protected V CreateValue(K key) { return builder.Factory != null ? builder.Factory(key) : builder.FactoryAsync(key).Result; } /// <summary> /// Creates a task for value for a key. /// </summary> /// <param name="key">A key to create value for.</param> /// <returns>A task for a value instance.</returns> protected Task<V> CreateValueAsync(K key) { return builder.FactoryAsync != null ? builder.FactoryAsync(key) : Task.FromResult(builder.Factory(key)); } /// <summary> /// Validates the value. /// </summary> /// <param name="value">A value to validate.</param> /// <returns> /// true if value is valid for a cache, and false otherise. /// </returns> protected bool IsValid(V value) { return (value != null) && ((builder.Validator == null) || builder.Validator(value)); } /// <summary> /// Set implementation. /// </summary> /// <param name="key">A key to set value for.</param> /// <param name="value">A value to set.</param> /// <returns>A set value.</returns> private V SetImpl(string key, V value) { if (value == null) { builder.MemoryCache.Remove(key); } else { builder.MemoryCache.Set( key, value, builder.CachePolicyFunc != null ? builder.CachePolicyFunc(value) : builder.Sliding ? new CacheItemPolicy { SlidingExpiration = builder.Expiration } : new CacheItemPolicy { AbsoluteExpiration = DateTime.Now + builder.Expiration }); } return value; } /// <summary> /// Cache builder. /// </summary> private Builder builder; }
The use consists of initialization:
Cache<MyKey, MyValue> MyValues = new Cache<MyKey, MyValue>.Builder { KeyFunc = key => ...key to string value..., Factory = key => ...create a value for a key..., Expiration = new TimeSpan(0, 3, 0), Sliding = true };
and a trivial cache access:
var value = MyValues[key];
This contrasts with MemoryCache coding pattern:
MemoryCache cache = MemoryCache.Default; ...
var keyAsString = ...key to string value... var value = cache.Get(keyAsString) as MyValue; if (value == null) { value = ...create a value for a key... cache.Set(keyAsString, value, ...caching policy...); }
Remember Me
a@href@title, b, blockquote@cite, em, i, strike, strong, sub, super, u