The following sample code shows how to create a Generic Memory Cache for .Net Framework.
This allows you to cache specific items defined by a TCacheItemData type argument, i.e. caching same type of data such as instances of a class, or arrays of instances.
Inside your .csproj you should see something like:
Over to the implementation. Since a memory cache is shared by possibly other applications, it is important to prefix your cached contents, i.e. prefix the the keys.
This makes it easier to barrier the memory cache. Note though that some barriering is done accross processes of course, this is just to make it easier within your application and running process
to group the cached elements with a prefix key used for the generic memory cache operations.
Now over to the implementation.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Caching;
namespaceSomeAcme.SomeUtilNamespace
{
///<summary>/// Thread safe memory cache for generic use///</summary>///<typeparam name="TCacheItemData">Payload to store in the memory cache</typeparam>///<remarks>Uses MemoryCache.Default which defaults to an in-memory cache. All cache items are prefixed with an 'import cache session guid' to compartmentalize/// multiple paralell importing sessions</remarks>publicclassGenericMemoryCache<TCacheItemData> whereTCacheItemData : class
{
privatereadonlystring _prefixKey;
privatereadonly ObjectCache _cache;
privatereadonly CacheItemPolicy _cacheItemPolicy;
publicGenericMemoryCache(string prefixKey, int defaultExpirationInSeconds = 0)
{
defaultExpirationInSeconds = Math.Abs(defaultExpirationInSeconds); //checking if a negative value was passed into the constructor.
_prefixKey = prefixKey;
_cache = MemoryCache.Default;
_cacheItemPolicy = defaultExpirationInSeconds == 0
? new CacheItemPolicy { Priority = CacheItemPriority.NotRemovable }
: new CacheItemPolicy
{ AbsoluteExpiration = DateTime.Now.AddSeconds(Math.Abs(defaultExpirationInSeconds)) };
}
///<summary>/// Cache object if direct access is desired///</summary>public ObjectCache Cache => _cache;
publicstringPrefixKey(string key) => $"{_prefixKey}_{key}";
///<summary>/// Adds an item to memory cache///</summary>///<param name="key"></param>///<param name="itemToCache"></param>///<returns></returns>publicboolAddItem(string key, TCacheItemData itemToCache)
{
try
{
if (!key.StartsWith(_prefixKey))
key = PrefixKey(key);
var cacheItem = new CacheItem(key, itemToCache);
_cache.Add(cacheItem, _cacheItemPolicy);
returntrue;
}
catch (Exception err)
{
Debug.WriteLine(err);
returnfalse;
}
}
publicvirtualList<T> GetValues<T>()
{
List<T> list = new List<T>();
IDictionaryEnumerator cacheEnumerator = (IDictionaryEnumerator)((IEnumerable)_cache).GetEnumerator();
while (cacheEnumerator.MoveNext())
{
if (cacheEnumerator.Key == null)
continue;
if (cacheEnumerator.Key.ToString().StartsWith(_prefixKey))
list.Add((T)cacheEnumerator.Value);
}
return list;
}
///<summary>/// Retrieves a cache item. Possible to set the expiration of the cache item in seconds. ///</summary>///<param name="key"></param>///<returns></returns>public TCacheItemData GetItem(string key)
{
try
{
if (!key.StartsWith(_prefixKey))
key = PrefixKey(key);
if (_cache.Contains(key))
{
CacheItem cacheItem = _cache.GetCacheItem(key);
object cacheItemValue = cacheItem?.Value;
UpdateItem(key, cacheItemValue as TCacheItemData);
TCacheItemData item = _cache.Get(key) as TCacheItemData;
return item;
}
returnnull;
}
catch (Exception err)
{
Debug.WriteLine(err);
returnnull;
}
}
publicboolSetItem(string key, TCacheItemData itemToCache)
{
try
{
if (!key.StartsWith(_prefixKey))
key = PrefixKey(key);
if (GetItem(key) != null)
{
AddItem(key, itemToCache);
returntrue;
}
UpdateItem(key, itemToCache);
returntrue;
}
catch (Exception err)
{
Debug.WriteLine(err);
returnfalse;
}
}
///<summary>/// Updates an item in the cache and set the expiration of the cache item ///</summary>///<param name="key"></param>///<param name="itemToCache"></param>///<returns></returns>publicboolUpdateItem(string key, TCacheItemData itemToCache)
{
if (!key.StartsWith(_prefixKey))
key = PrefixKey(key);
CacheItem cacheItem = _cache.GetCacheItem(key);
if (cacheItem != null)
{
cacheItem.Value = itemToCache;
_cache.Set(key, itemToCache, _cacheItemPolicy);
}
else
{
//if we cant find the cache item, just set the cache directly
_cache.Set(key, itemToCache, _cacheItemPolicy);
}
returntrue;
}
///<summary>/// Removes an item from the cache ///</summary>///<param name="key"></param>///<returns></returns>publicboolRemoveItem(string key)
{
if (!key.StartsWith(_prefixKey))
key = PrefixKey(key);
if (_cache.Contains(key))
{
_cache.Remove(key);
returntrue;
}
returnfalse;
}
publicvoidAddItems(Dictionary<string, TCacheItemData> itemsToCache)
{
foreach (var kvp in itemsToCache)
AddItem(kvp.Key, kvp.Value);
}
///<summary>/// Clear all cache keys starting with known prefix passed into the constructor.///</summary>publicvoidClearAll()
{
var cacheKeys = _cache.Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
{
if (cacheKey.StartsWith(_prefixKey))
_cache.Remove(cacheKey);
}
}
}
}