using System;
using System.Collections.Generic;
using Microsoft.ApplicationServer.Caching;
using ServiceStack.Logging;
namespace ServiceStack.CacheAccess.Azure
{
public class AzureCacheClient : AdapterBase, ICacheClient
{
private DataCacheFactory CacheFactory { get; set; }
private DataCache DataCache { get; set; }
public bool FlushOnDispose { get; set; }
protected override ILog Log { get { return LogManager.GetLogger(GetType()); } }
public AzureCacheClient(string cacheName = null)
{
CacheFactory = new DataCacheFactory();
if(string.IsNullOrEmpty(cacheName))
DataCache = CacheFactory.GetDefaultCache();
else
DataCache = CacheFactory.GetCache(cacheName);
}
private bool TryGetValue(string key, out object entry)
{
entry = DataCache.Get(key);
return entry != null;
}
private bool CacheAdd(string key, object value)
{
return CacheAdd(key, value, DateTime.MaxValue);
}
///
/// Stores The value with key only if such key doesn't exist at the server yet.
///
/// The key.
/// The value.
/// The expires at.
///
private bool CacheAdd(string key, object value, DateTime expiresAt)
{
object entry;
if (TryGetValue(key, out entry)) return false;
DataCache.Add(key, value, expiresAt.Subtract(DateTime.Now));
return true;
}
private bool CacheSet(string key, object value)
{
return CacheSet(key, value, DateTime.MaxValue);
}
///
/// Adds or replaces the value with key. Return false if a version exists but is not the lastversion.
///
/// The key.
/// The value.
/// The expires at.
/// The check last version
/// True; if it succeeded
private bool CacheSet(string key, object value, DateTime expiresAt, DataCacheItemVersion checkLastVersion = null)
{
if (checkLastVersion != null)
{
object entry = DataCache.GetIfNewer(key, ref checkLastVersion);
if(entry != null)
{
//update value and version
DataCache.Put(key, value, checkLastVersion, expiresAt.Subtract(DateTime.Now));
return true;
}
if (TryGetValue(key, out entry))
{//version exists but is older.
return false;
}
}
//if we don't care about version, then just update
DataCache.Put(key, value, expiresAt.Subtract(DateTime.Now));
return true;
}
private bool CacheReplace(string key, object value)
{
return CacheReplace(key, value, DateTime.MaxValue);
}
private bool CacheReplace(string key, object value, DateTime expiresAt)
{
return !CacheSet(key, value, expiresAt);;
}
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
/// 2
public void Dispose()
{
if (!FlushOnDispose) return;
FlushAll();
}
///
/// Removes the specified item from the cache.
///
/// The identifier for the item to delete.
///
/// true if the item was successfully removed from the cache; false otherwise.
///
public bool Remove(string key)
{
return DataCache.Remove(key);
}
///
/// Removes the cache for all the keys provided.
///
/// The keys.
public void RemoveAll(IEnumerable keys)
{
foreach (var key in keys)
{
try
{
Remove(key);
}
catch(Exception ex)
{
Log.Error(string.Format("Error trying to remove {0} from azure cache", key), ex);
}
}
}
public object Get(string key)
{
DataCacheItemVersion version;
return Get(key, out version);
}
public object Get(string key, out DataCacheItemVersion version)
{
return DataCache.Get(key, out version);
}
///
/// Retrieves the specified item from the cache.
///
///
/// The identifier for the item to retrieve.
///
/// The retrieved item, or null if the key was not found.
///
public T Get(string key)
{
var value = Get(key);
if (value != null) return (T)value;
return default(T);
}
///
/// Increments the value of the specified key by the given amount.
/// The operation is atomic and happens on the server.
/// A non existent value at key starts at 0
///
/// The identifier for the item to increment.
/// The amount by which the client wants to increase the item.
///
/// The new value of the item or -1 if not found.
///
/// The item must be inserted into the cache before it can be changed. The item must be inserted as a . The operation only works with values, so -1 always indicates that the item was not found.
public long Increment(string key, uint amount)
{
return UpdateCounter(key, (int) amount);
}
private long UpdateCounter(string key, int value)
{
long longVal;
if(Int64.TryParse(Get(key).ToString(), out longVal))
{
longVal += value;
CacheSet(key, longVal);
return longVal;
}
CacheSet(key, 0);
return 0;
}
///
/// Increments the value of the specified key by the given amount.
/// The operation is atomic and happens on the server.
/// A non existent value at key starts at 0
///
/// The identifier for the item to increment.
/// The amount by which the client wants to decrease the item.
///
/// The new value of the item or -1 if not found.
///
/// The item must be inserted into the cache before it can be changed. The item must be inserted as a . The operation only works with values, so -1 always indicates that the item was not found.
public long Decrement(string key, uint amount)
{
return UpdateCounter(key, (int)-amount);
}
///
/// Adds a new item into the cache at the specified cache key only if the cache is empty.
///
/// The key used to reference the item.
/// The object to be inserted into the cache.
///
/// true if the item was successfully stored in the cache; false otherwise.
///
/// The item does not expire unless it is removed due memory pressure.
public bool Add(string key, T value)
{
return CacheAdd(key, value);
}
///
/// Sets an item into the cache at the cache key specified regardless if it already exists or not.
///
public bool Set(string key, T value)
{
return CacheSet(key, value);
}
///
/// Replaces the item at the cachekey specified only if an items exists at the location already.
///
public bool Replace(string key, T value)
{
return CacheReplace(key, value);
}
public bool Add(string key, T value, DateTime expiresAt)
{
return CacheAdd(key, value, expiresAt);
}
public bool Set(string key, T value, DateTime expiresAt)
{
return CacheSet(key, value, expiresAt);
}
public bool Replace(string key, T value, DateTime expiresAt)
{
return CacheReplace(key, value, expiresAt);
}
public bool Add(string key, T value, TimeSpan expiresIn)
{
return CacheAdd(key, value, DateTime.Now.Add(expiresIn));
}
public bool Set(string key, T value, TimeSpan expiresIn)
{
return CacheSet(key, value, DateTime.Now.Add(expiresIn));
}
public bool Replace(string key, T value, TimeSpan expiresIn)
{
return CacheReplace(key, value, DateTime.Now.Add(expiresIn));
}
///
/// Invalidates all data on the cache.
///
public void FlushAll()
{
var regions = DataCache.GetSystemRegions();
foreach (var region in regions)
{
DataCache.ClearRegion(region);
}
}
///
/// Retrieves multiple items from the cache.
/// The default value of T is set for all keys that do not exist.
///
/// The list of identifiers for the items to retrieve.
///
/// a Dictionary holding all items indexed by their key.
///
public IDictionary GetAll(IEnumerable keys)
{
var valueMap = new Dictionary();
foreach (var key in keys)
{
var value = Get(key);
valueMap[key] = value;
}
return valueMap;
}
///
/// Sets multiple items to the cache.
///
///
/// The values.
public void SetAll(IDictionary values)
{
foreach (var entry in values)
{
Set(entry.Key, entry.Value);
}
}
}
}