/// <summary> /// Attempts to retrieve an item with the specified ID, returning success or /// failure. /// </summary> /// <typeparam name="T"> /// The type of the item to retrieve. /// </typeparam> /// <param name="id"> /// The ID of the item to retrieve. /// </param> /// <param name="value"> /// Output parameter. With contain the retrieved value if a match is found, /// or the default value otherwise. /// </param> /// <returns> /// <see langword="true" /> if a matching item was successfully retrieved; /// otherwise <see langword="false" />. /// </returns> public bool TryGetValue <T>(IConvertible id, out T value) { Contract.Requires(id != null, Resources.Messages.EveCache_IdCannotBeNull); Contract.Ensures(!Contract.Result <bool>() || Contract.ValueAtReturn <T>(out value) != null); string region = RegionMap.GetRegion(typeof(T)); string key = EveCache.CreateCacheKey(region, id); this.EnterReadLock(region); try { object result; if (this.InnerTryGetValue(key, out result)) { value = (T)result; return(true); } } finally { this.ExitReadLock(region); } this.Statistics.Misses++; value = default(T); return(false); }
/// <summary> /// Given a type, constructs a string to represent the cache region key /// for that type's cache domain. /// </summary> /// <param name="type"> /// The type for which to generate a cache region key. /// </param> /// <returns> /// A <see cref="String" /> containing a generated cache region key for /// <paramref name="type" />. /// </returns> /// <remarks> /// <para> /// The method is not guaranteed to return the same value for repeated /// calls with the same type. The returned value is cached after the /// first call for a given type, and the method is never called again /// for that type. /// </para> /// </remarks> private static string ConstructRegionForType(Type type) { Contract.Requires(type != null, "The type cannot be null."); Contract.Ensures(Contract.Result <string>() != null); byte[] bytes = BitConverter.GetBytes(Interlocked.Increment(ref currentKeyIndex)); if (BitConverter.IsLittleEndian) { Array.Reverse(bytes); } return(EveCache.ByteArrayToShortString(bytes)); }
/// <summary> /// Retrieves the item with the specified ID from the cache, or, if no /// matching item is present, adds the specified value to the cache and /// returns it. /// </summary> /// <typeparam name="T"> /// The type of item to add to or retrieve from the cache. /// </typeparam> /// <param name="value"> /// The value which will be added and returned if a matching item cannot /// be found in the cache. /// </param> /// <returns> /// The cached item with the same key as <paramref name="value" />, if /// such an item exists. Otherwise, <paramref name="value" /> will be /// added to the cache and then returned. /// </returns> public T GetOrAdd <T>(T value) where T : IEveCacheable { Contract.Requires(value != null, Resources.Messages.EveCache_ValueCannotBeNull); Contract.Ensures(Contract.Result <T>() != null); string region = RegionMap.GetRegion(value.GetType()); string key = EveCache.CreateCacheKey(region, value.CacheKey); this.EnterReadLock(region); try { object result; if (this.InnerTryGetValue(key, out result)) { return((T)result); } } finally { this.ExitReadLock(region); } // Otherwise, write to the cache this.EnterWriteLock(region); try { object result; if (this.InnerTryGetValue(key, out result)) { return((T)result); } this.Statistics.Misses++; this.InnerSet(key, value, false); return(value); } finally { this.ExitWriteLock(region); } }
/// <summary> /// Returns a value indicating whether an item with the specified ID is /// contained in the cache. /// </summary> /// <typeparam name="T"> /// The type of the item to locate. /// </typeparam> /// <param name="id"> /// The ID to locate in the cache. /// </param> /// <returns> /// <see langword="true" /> if an item with the specified ID is contained /// in the cache; otherwise <see langword="false" />. /// </returns> public bool Contains <T>(IConvertible id) { Contract.Requires(id != null, Resources.Messages.EveCache_IdCannotBeNull); string region = RegionMap.GetRegion(typeof(T)); string key = EveCache.CreateCacheKey(region, id); this.EnterReadLock(region); try { return(this.InnerContains(key)); } finally { this.ExitReadLock(region); } }
/// <summary> /// Add the item to the cache, replacing any existing item with the same ID. /// </summary> /// <typeparam name="T"> /// The type of item to add to the cache. /// </typeparam> /// <param name="value"> /// The item to add to the cache. /// </param> /// <param name="permanent"> /// Specifies whether to add the value permanently or whether it can be /// automatically evicted. /// </param> /// <remarks> /// <para> /// Items added with <paramref name="permanent" /> equal to /// <see langword="true" /> are immune to automatic eviction, but can /// still be removed or overwritten manually. /// </para> /// </remarks> public void AddOrReplace <T>(T value, bool permanent) where T : IEveCacheable { Contract.Requires(value != null, Resources.Messages.EveCache_ValueCannotBeNull); string region = RegionMap.GetRegion(typeof(T)); string key = EveCache.CreateCacheKey(region, value.CacheKey); this.EnterWriteLock(region); try { this.InnerSet(key, value, permanent); } finally { this.ExitWriteLock(region); } }
/// <summary> /// Removes the item with the specified key. /// </summary> /// <typeparam name="T"> /// The type of the item to remove. /// </typeparam> /// <param name="id"> /// The ID of the item to remove. /// </param> /// <returns> /// The removed item, or the default value if no matching item was found. /// </returns> public T Remove <T>(IConvertible id) { Contract.Requires(id != null, Resources.Messages.EveCache_IdCannotBeNull); string region = RegionMap.GetRegion(typeof(T)); string key = EveCache.CreateCacheKey(region, id); this.EnterWriteLock(region); try { var result = this.InnerRemove(key); return((result == null) ? default(T) : (T)result); } finally { this.ExitWriteLock(region); } }
/// <summary> /// Retrieves the item with the specified ID from the cache, or, if no /// matching item is present, adds the specified value to the cache and /// returns it. /// </summary> /// <typeparam name="T"> /// The type of item to add to or retrieve from the cache. /// </typeparam> /// <param name="id"> /// The ID of the item to add or retrieve. /// </param> /// <param name="valueFactory"> /// The <see cref="Func{TOutput}" /> which will generate the value to be /// added if a matching item cannot be found in the cache. /// </param> /// <returns> /// The item of the desired type and with the specified key, if a matching /// item is contained in the cache. Otherwise, the result of executing /// <paramref name="valueFactory" />. /// </returns> public T GetOrAdd <T>(IConvertible id, Func <T> valueFactory) where T : IEveCacheable { Contract.Requires(id != null, Resources.Messages.EveCache_IdCannotBeNull); Contract.Requires(valueFactory != null, Resources.Messages.EveCache_ValueFactoryCannotBeNull); Contract.Ensures(Contract.Result <T>() != null); string region = RegionMap.GetRegion(typeof(T)); string key = EveCache.CreateCacheKey(region, id); this.EnterReadLock(region); try { object result; if (this.InnerTryGetValue(key, out result)) { return((T)result); } } finally { this.ExitReadLock(region); } // Otherwise, get our value to be added. Do this outside of a lock in // case valueFactory() itself wants to read from or add something to the // cache. T value = valueFactory(); Contract.Assume(value != null); // Check to make sure the value being added actually has the same key as // the value we were passed -- otherwise the cache could be put into an // inconsistent state. string verifyKey = EveCache.CreateCacheKey(region, value.CacheKey); if (!object.Equals(key, verifyKey)) { throw new InvalidOperationException("The key of the item being added to the cache must be the same as the key being requested."); } // Write to the cache this.EnterWriteLock(region); try { object result; if (this.InnerTryGetValue(key, out result)) { return((T)result); } this.Statistics.Misses++; this.InnerSet(key, value, false); return(value); } finally { this.ExitWriteLock(region); } }