Skip to content

Instantly share code, notes, and snippets.

@dealproc
Last active August 31, 2016 22:58
Show Gist options
  • Save dealproc/d7144477c2767c8de89253ad8a87f39e to your computer and use it in GitHub Desktop.
Save dealproc/d7144477c2767c8de89253ad8a87f39e to your computer and use it in GitHub Desktop.
Caching (in-memory and redis)
using System;
namespace {yourapp}.Infrastructure.Caching {
public interface ICache {
/// <summary>
/// Determines whether the specified keyformat contains key.
/// </summary>
/// <param name="keyformat">The keyformat.</param>
/// <param name="arguments">The arguments.</param>
/// <returns></returns>
bool ContainsKey(string keyformat, params object[] arguments);
/// <summary>
/// Clears the specified item from the cache.
/// </summary>
/// <param name="keyFormat">The key format.</param>
/// <param name="arguments">The arguments for the key format.</param>
void Clear(string keyFormat, params object[] arguments);
/// <summary>
/// Adds the specified data to the cache for the specified time.
/// </summary>
/// <param name="keyFormat">The key format.</param>
/// <param name="data">The data.</param>
/// <param name="slidingExpiration">The sliding expiration.</param>
/// <param name="arguments">The arguments for the key format.</param>
void Add<T>(string keyFormat, T data, TimeSpan slidingExpiration, params object[] arguments);
/// <summary>
/// Adds the specified data to the cache until specified time.
/// </summary>
/// <param name="keyFormat">The key format.</param>
/// <param name="data">The data.</param>
/// <param name="absoluteExpiration">The absolute expiration.</param>
/// <param name="arguments">The arguments for the key format.</param>
void Add<T>(string keyFormat, T data, DateTime absoluteExpiration, params object[] arguments);
/// <summary>
/// Retrieves the data for the specified key.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="keyFormat">The key format.</param>
/// <param name="arguments">The arguments for the key format.</param>
/// <returns>The data for the key if it exists; null otherwise.</returns>
CachedObject<T> Retrieve<T>(string keyFormat, params object[] arguments) where T : class;
/// <summary>
/// Retrieves the data for the specified key.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="keyFormat">The key format.</param>
/// <param name="retrievalMethod">The method that returns the data in the event it is not cached.</param>
/// <param name="arguments">The arguments for the key format.</param>
/// <returns>The data for the key if it exists; null otherwise.</returns>
CachedObject<T> Retrieve<T>(Func<T> retrievalMethod, string keyFormat, params object[] arguments) where T : class;
/// <summary>
/// Retrieves the data for the specified key. The data will be cached on a sliding expiration for the specified timeout
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="retrievalMethod">The method that returns the data in the event it is not cached.</param>
/// <param name="slidingExpiration">The sliding expiration.</param>
/// <param name="keyFormat">The key format.</param>
/// <param name="arguments">The arguments for the key format.</param>
/// <returns>
/// The data for the key if it exists; null otherwise.
/// </returns>
CachedObject<T> Retrieve<T>(Func<T> retrievalMethod, TimeSpan slidingExpiration, string keyFormat, params object[] arguments) where T : class;
/// <summary>
/// Retrieves the data for the specified key. The data will be cached until the date specified
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="retrievalMethod">The retrieval method.</param>
/// <param name="absoluteExpiration">The absolute expiration.</param>
/// <param name="keyFormat">The key format.</param>
/// <param name="arguments">The arguments.</param>
/// <returns></returns>
CachedObject<T> Retrieve<T>(Func<T> retrievalMethod, DateTime absoluteExpiration, string keyFormat, params object[] arguments) where T : class;
/// <summary>
/// Resets the specified key format.
/// </summary>
/// <param name="keyFormat">The key format.</param>
/// <param name="arguments">The arguments.</param>
void Reset(string keyFormat, params object[] arguments);
}
/// <summary>
/// Indicate the relative priority of this item of keeping the item cached.
/// </summary>
public enum RelativeCachePriority {
/// <summary>
/// Cache items with this priority level are the most likely to be deleted from the cache as the
/// server frees system memory.
/// </summary>
Low = 1,
/// <summary>
/// Cache items with this priority level are more likely to be deleted from the cache as the
/// server frees system memory than items assigned a Normal priority.
/// </summary>
BelowNormal = 2,
/// <summary>
/// Cache items with this priority level are likely to be deleted from the cache
/// as the server frees system memory only after those items with Low
/// or BelowNormal priority. This is the default.
/// </summary>
Normal = 3,
/// <summary>
/// Cache items with this priority level are less likely to be deleted as the
/// server frees system memory than those assigned a Normal priority.
/// </summary>
AboveNormal = 4,
/// <summary>
/// Cache items with this priority level are the least likely to be deleted from
/// the cache as the server frees system memory.
/// </summary>
High = 5,
/// <summary>
/// The cache items with this priority level will not be automatically deleted
/// from the cache as the server frees system memory
/// </summary>
NotRemovable = 6,
}
/// <summary>
/// Objects of this class type are objects that have been inserted into the cache.
/// This type tells you when that object was created.
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
public class CachedObject<T> {
private CachedObject() { }
/// <summary>
/// Initializes a new instance of the <see cref="CachedObject{T}"/> class.
/// </summary>
/// <param name="o">The o.</param>
public CachedObject(T o) {
Object = o;
CacheDate = DateTime.Now;
}
/// <summary>
/// Initializes a new instance of the <see cref="CachedObject{T}"/> class.
/// </summary>
/// <param name="o">The o.</param>
/// <param name="cacheDate">The cache date.</param>
public CachedObject(T o, DateTime cacheDate) {
Object = o;
CacheDate = cacheDate;
}
/// <summary>
/// Gets the cache date.
/// </summary>
/// <value>
/// The cache date.
/// </value>
public DateTime CacheDate { get; private set; }
/// <summary>
/// Gets the object.
/// </summary>
/// <value>
/// The object.
/// </value>
public T Object { get; private set; }
}
}
using System;
using System.Runtime.Caching;
namespace {yourapp}.Infrastructure.Caching {
public class MemoryCacheWrapper : ICache {
private static readonly ObjectCache Cache = MemoryCache.Default;
private readonly object _pad = new object();
/// <summary>
/// Determines whether the this cache contains the specified key.
/// </summary>
/// <param name="keyformat">The keyformat.</param>
/// <param name="arguments">The arguments.</param>
/// <returns></returns>
public bool ContainsKey(string keyformat, params object[] arguments) {
return Cache.Get(Key(keyformat, arguments)) != null;
}
/// <summary>
/// Clears the specified item from the cache.
/// </summary>
/// <param name="keyFormat">The key format.</param>
/// <param name="arguments">The arguments for the key format.</param>
public void Clear(string keyFormat, params object[] arguments) {
Cache.Remove(Key(keyFormat, arguments));
}
/// <summary>
/// Adds the specified data to the cache for the specified time.
/// </summary>
/// <param name="keyFormat">The key format.</param>
/// <param name="data">The data.</param>
/// <param name="slidingExpiration">The sliding expiration.</param>
/// <param name="arguments">The arguments for the key format.</param>
public void Add<T>(string keyFormat, T data, TimeSpan slidingExpiration, params object[] arguments) {
var policy = new CacheItemPolicy { SlidingExpiration = slidingExpiration };
var cacheable = new CachedObject<T>(data);
Cache.Set(Key(keyFormat, arguments), cacheable, policy);
}
/// <summary>
/// Adds the specified data to the cache for the specified time.
/// </summary>
/// <param name="keyFormat">The key format.</param>
/// <param name="data">The data.</param>
/// <param name="absoluteExpiration">The absolute expiration.</param>
/// <param name="arguments">The arguments for the key format.</param>
public void Add<T>(string keyFormat, T data, DateTime absoluteExpiration, params object[] arguments) {
var policy = new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration };
var cacheable = new CachedObject<T>(data);
Cache.Set(Key(keyFormat, arguments), cacheable, policy);
}
/// <summary>
/// Retrieves the data for the specified key.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="keyFormat">The key format.</param>
/// <param name="arguments">The arguments for the key format.</param>
/// <returns>The data for the key if it exists; null otherwise.</returns>
public CachedObject<T> Retrieve<T>(string keyFormat, params object[] arguments) where T : class {
var obj = Cache.Get(Key(keyFormat, arguments));
if (obj is CachedObject<T>)
return obj as CachedObject<T>;
if (obj is T)
return new CachedObject<T>(obj as T, DateTime.MinValue);
return null;
}
/// <summary>
/// Retrieves the data for the specified key.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="keyFormat">The key format.</param>
/// <param name="retrievalMethod">The method that returns the data in the event it is not cached.</param>
/// <param name="arguments">The arguments for the key format.</param>
/// <returns>The data for the key if it exists; null otherwise.</returns>
public CachedObject<T> Retrieve<T>(Func<T> retrievalMethod, string keyFormat, params object[] arguments) where T : class {
var policy = new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(15) }; //Default 15 min
return Retrieve(retrievalMethod, policy, keyFormat, arguments);
}
/// <summary>
/// Retrieves the data for the specified key.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="keyFormat">The key format.</param>
/// <param name="slidingExpiration">The sliding expiration.</param>
/// <param name="retrievalMethod">The method that returns the data in the event it is not cached.</param>
/// <param name="arguments">The arguments for the key format.</param>
/// <returns>The data for the key if it exists; null otherwise.</returns>
public CachedObject<T> Retrieve<T>(Func<T> retrievalMethod, TimeSpan slidingExpiration, string keyFormat, params object[] arguments) where T : class {
var policy = new CacheItemPolicy { SlidingExpiration = slidingExpiration };
return Retrieve(retrievalMethod, policy, keyFormat, arguments);
}
/// <summary>
/// Retrieves the data for the specified key. The data will be cached on a absolute expiration for the specified timeout
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="retrievalMethod">The retrieval method.</param>
/// <param name="absoluteExpiration">The absolute expiration.</param>
/// <param name="keyFormat">The keyformat.</param>
/// <param name="arguments">The arguments.</param>
/// <returns></returns>
public CachedObject<T> Retrieve<T>(Func<T> retrievalMethod, DateTime absoluteExpiration, string keyFormat, params object[] arguments) where T : class {
var policy = new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration };
return Retrieve(retrievalMethod, policy, keyFormat, arguments);
}
/// <summary>
/// Resets the cache item(s).
/// </summary>
/// <param name="keyFormat">The key format.</param>
/// <param name="arguments">The arguments.</param>
public void Reset(string keyFormat, params object[] arguments) {
this.Clear(keyFormat, arguments);
}
private CachedObject<T> Retrieve<T>(Func<T> retrievalMethod, CacheItemPolicy policy, string keyFormat, params object[] arguments) where T : class {
var key = Key(keyFormat, arguments);
var item = Cache.Get(key) as CachedObject<T>;
if (item != null)
return item;
lock (_pad) {
item = Cache.Get(key) as CachedObject<T>;
if (item != null)
return item;
var obj = retrievalMethod();
if (obj != null) {
item = new CachedObject<T>(obj);
Cache.Set(key, item, policy);
}
}
return item;
}
private static string Key(string format, params object[] arguments) {
return String.Format(format, arguments);
}
}
}
using System;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using StackExchange.Redis;
namespace {yourapp}.Infrastructure.Caching {
public class RedisCache : ICache {
readonly object _pad = new object();
readonly IDatabase _database;
readonly JsonSerializerSettings _settings;
public RedisCache(Lazy<ConnectionMultiplexer> connectionFactory) {
_database = connectionFactory.Value.GetDatabase();
var resolver = new JsonPrivatePropertiesContractResolver();
_settings = new JsonSerializerSettings { ContractResolver = resolver };
}
public void Add<T>(string keyFormat, T data, DateTime absoluteExpiration, params object[] arguments) {
var key = Key(keyFormat, arguments);
var cacheable = new CachedObject<T>(data);
_database.StringSet(key, JsonConvert.SerializeObject(cacheable));
_database.KeyExpire(key, absoluteExpiration);
}
public void Add<T>(string keyFormat, T data, TimeSpan slidingExpiration, params object[] arguments) {
var key = Key(keyFormat, arguments);
var cacheable = new CachedObject<T>(data);
_database.StringSet(key, JsonConvert.SerializeObject(cacheable));
_database.KeyExpire(key, CalculateSlidingExpiration(slidingExpiration));
}
public void Clear(string keyFormat, params object[] arguments) {
_database.KeyDelete(Key(keyFormat, arguments));
}
public bool ContainsKey(string keyformat, params object[] arguments) {
return _database.KeyExists(Key(keyformat, arguments));
}
public void Reset(string keyFormat, params object[] arguments) {
_database.KeyDelete(Key(keyFormat, arguments));
}
public CachedObject<T> Retrieve<T>(string keyFormat, params object[] arguments) where T : class {
RedisValue str = default(RedisValue);
str = _database.StringGet(Key(keyFormat, arguments));
if (str.HasValue) {
return JsonConvert.DeserializeObject<CachedObject<T>>(str, _settings);
}
return null;
}
public CachedObject<T> Retrieve<T>(Func<T> retrievalMethod, string keyFormat, params object[] arguments) where T : class {
return RetrieveWithSlidingExpiration(retrievalMethod, TimeSpan.FromMinutes(15), keyFormat, arguments);
}
public CachedObject<T> Retrieve<T>(Func<T> retrievalMethod, DateTime absoluteExpiration, string keyFormat, params object[] arguments) where T : class {
return RetrieveWithAbsoluteExpiration(retrievalMethod, absoluteExpiration, keyFormat, arguments);
}
public CachedObject<T> Retrieve<T>(Func<T> retrievalMethod, TimeSpan slidingExpiration, string keyFormat, params object[] arguments) where T : class {
return RetrieveWithSlidingExpiration(retrievalMethod, slidingExpiration, keyFormat, arguments);
}
private CachedObject<T> RetrieveWithAbsoluteExpiration<T>(Func<T> howToRetrieve, DateTime absoluteExpiration, string keyFormat, params object[] keyFormatArguments) where T : class {
var key = Key(keyFormat, keyFormatArguments);
CachedObject<T> item = null;
RedisValue str = default(RedisValue);
str = _database.StringGet(key);
if (!str.IsNull) {
item = JsonConvert.DeserializeObject<CachedObject<T>>(str, _settings);
} else {
lock (_pad) {
str = _database.StringGet(key);
if (!str.IsNull) {
item = JsonConvert.DeserializeObject<CachedObject<T>>(str, _settings);
} else {
var obj = howToRetrieve();
if (obj != null) {
item = new CachedObject<T>(obj);
_database.StringSet(key, JsonConvert.SerializeObject(item));
_database.KeyExpire(key, absoluteExpiration);
}
}
}
}
return item;
}
private CachedObject<T> RetrieveWithSlidingExpiration<T>(Func<T> howToRetrieve, TimeSpan slidingExpiration, string keyFormat, params object[] keyFormatArguments) where T : class {
var absToExpire = CalculateSlidingExpiration(slidingExpiration);
var item = RetrieveWithAbsoluteExpiration(howToRetrieve, absToExpire, keyFormat, keyFormatArguments);
if (item != null) {
_database.KeyExpire(Key(keyFormat, keyFormatArguments), absToExpire);
}
return item;
}
private static string Key(string format, params object[] arguments) {
return string.Format(format, arguments);
}
private static DateTime CalculateSlidingExpiration(TimeSpan slidingExpiration) {
return DateTime.Now.Add(slidingExpiration);
}
}
class JsonPrivatePropertiesContractResolver : DefaultContractResolver {
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
var prop = base.CreateProperty(member, memberSerialization);
if (!prop.Writable) {
var property = member as PropertyInfo;
if (property != null) {
var hasPrivateSetter = property.GetSetMethod(true) != null;
prop.Writable = hasPrivateSetter;
}
}
return prop;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment