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); } } } }