1: /// <summary>
2: /// Windows Azure AppFabric Cache implementation of ObjectCache
3: /// </summary>
4: public class AppFabricCacheProvider : ObjectCache, IDisposable
5: {
6: private DataCacheFactory _cacheFactory;
7:
8: private const string RegionKeyTemplate = "{0}:{1}";
9:
10: /// <summary>
11: /// Initializes a new instance of the <see cref="AppFabricCacheProvider"/> class.
12: /// </summary>
13: public AppFabricCacheProvider()
14: {
15: _cacheFactory = new DataCacheFactory();
16: }
17:
18: public override string Name
19: {
20: get { return "AppFabricCacheService"; }
21: }
22:
23: public override DefaultCacheCapabilities DefaultCacheCapabilities
24: {
25: get { return DefaultCacheCapabilities.OutOfProcessProvider | DefaultCacheCapabilities.AbsoluteExpirations | DefaultCacheCapabilities.SlidingExpirations; }
26: }
27:
28: public override object this[string key]
29: {
30: get
31: {
32: return this.Get(key, null);
33: }
34:
35: set
36: {
37: throw new NotSupportedException();
38: }
39: }
40:
41: /// <summary>
42: /// When overridden in a derived class, inserts a cache entry into the cache, specifying a key and a value for the cache entry, and information about how the entry will be evicted.
43: /// </summary>
44: /// <param name="key">A unique identifier for the cache entry.</param>
45: /// <param name="value">The object to insert.</param>
46: /// <param name="policy">An object that contains eviction details for the cache entry. This object provides more options for eviction than a simple absolute expiration.</param>
47: /// <param name="regionName">Optional. A named region in the cache to which the cache entry can be added, if regions are implemented. The default value for the optional parameter is null.</param>
48: /// <returns>
49: /// If a cache entry with the same key exists, the specified cache entry's value; otherwise, null.
50: /// </returns>
51: public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
52: {
53: return this.AddOrGetExisting(new CacheItem(key, value, regionName), policy).Value;
54: }
55:
56: /// <summary>
57: /// When overridden in a derived class, inserts a cache entry into the cache, by using a key, an object for the cache entry, an absolute expiration value, and an optional region to add the cache into.
58: /// </summary>
59: /// <param name="key">A unique identifier for the cache entry.</param>
60: /// <param name="value">The object to insert.</param>
61: /// <param name="absoluteExpiration">The fixed date and time at which the cache entry will expire.</param>
62: /// <param name="regionName">Optional. A named region in the cache to which the cache entry can be added, if regions are implemented. The default value for the optional parameter is null.</param>
63: /// <returns>
64: /// If a cache entry with the same key exists, the specified cache entry's value; otherwise, null.
65: /// </returns>
66: public override object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
67: {
68: return this.AddOrGetExisting(new CacheItem(key, value, regionName), new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration }).Value;
69: }
70:
71: /// <summary>
72: /// When overridden in a derived class, inserts the specified <see cref="T:System.Runtime.Caching.CacheItem"/> object into the cache, specifying information about how the entry will be evicted.
73: /// </summary>
74: /// <param name="value">The object to insert.</param>
75: /// <param name="policy">An object that contains eviction details for the cache entry. This object provides more options for eviction than a simple absolute expiration.</param>
76: /// <returns>
77: /// If a cache entry with the same key exists, the specified cache entry; otherwise, null.
78: /// </returns>
79: public override CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy)
80: {
81: var data = this.Get(value.Key, value.RegionName);
82: if (data != null)
83: {
84: return new CacheItem(value.Key, data, value.RegionName);
85: }
86:
87: this.Set(value, policy);
88: return value;
89: }
90:
91: /// <summary>
92: /// When overridden in a derived class, tries to insert a cache entry into the cache as a <see cref="T:System.Runtime.Caching.CacheItem"/> instance, and adds details about how the entry should be evicted.
93: /// </summary>
94: /// <param name="item">The object to add.</param>
95: /// <param name="policy">An object that contains eviction details for the cache entry. This object provides more options for eviction than a simple absolute expiration.</param>
96: /// <returns>
97: /// true if insertion succeeded, or false if there is an already an entry in the cache that has the same key as <paramref name="item"/>.
98: /// </returns>
99: public override bool Add(CacheItem item, CacheItemPolicy policy)
100: {
101: if (item.Value == null)
102: return false;
103:
104: if (policy != null)
105: return base.Add(item, policy);
106:
107: try
108: {
109: Set(item, null);
110: }
111: catch (Exception)
112: {
113: return false;
114: }
115: return true;
116: }
117:
118: /// <summary>
119: /// When overridden in a derived class, gets the specified cache entry from the cache as an object.
120: /// </summary>
121: /// <param name="key">A unique identifier for the cache entry to get.</param>
122: /// <param name="regionName">Optional. A named region in the cache to which the cache entry was added, if regions are implemented. The default value for the optional parameter is null.</param>
123: /// <returns>
124: /// The cache entry that is identified by <paramref name="key"/>.
125: /// </returns>
126: public override object Get(string key, string regionName = null)
127: {
128: try
129: {
130: return _cacheFactory.GetDefaultCache().Get(GetKey(key, regionName));
131: }
132: catch (DataCacheException ex)
133: {
134: if (ex.ErrorCode == DataCacheErrorCode.RetryLater)
135: {
136: // temporal failure, ignore and continue
137: return null;
138: }
139:
140: throw;
141: }
142: }
143:
144: /// <summary>
145: /// When overridden in a derived class, checks whether the cache entry already exists in the cache.
146: /// </summary>
147: /// <param name="key">A unique identifier for the cache entry.</param>
148: /// <param name="regionName">Optional. A named region in the cache where the cache can be found, if regions are implemented. The default value for the optional parameter is null.</param>
149: /// <returns>
150: /// true if the cache contains a cache entry with the same key value as <paramref name="key"/>; otherwise, false.
151: /// </returns>
152: public override bool Contains(string key, string regionName = null)
153: {
154: return this.Get(key, regionName) != null;
155: }
156:
157: /// <summary>
158: /// When overridden in a derived class, gets the specified cache entry from the cache as a <see cref="T:System.Runtime.Caching.CacheItem"/> instance.
159: /// </summary>
160: /// <param name="key">A unique identifier for the cache entry to get.</param>
161: /// <param name="regionName">Optional. A named region in the cache to which the cache was added, if regions are implemented. Because regions are not implemented in .NET Framework 4, the default is null.</param>
162: /// <returns>
163: /// The cache entry that is identified by <paramref name="key"/>.
164: /// </returns>
165: public override CacheItem GetCacheItem(string key, string regionName = null)
166: {
167: var data = this.Get(key, regionName);
168: if (data != null)
169: {
170: return new CacheItem(key, data, regionName); ;
171: }
172:
173: return null;
174: }
175:
176: /// <summary>
177: /// When overridden in a derived class, removes the cache entry from the cache.
178: /// </summary>
179: /// <param name="key">A unique identifier for the cache entry.</param>
180: /// <param name="regionName">Optional. A named region in the cache to which the cache entry was added, if regions are implemented. The default value for the optional parameter is null.</param>
181: /// <returns>
182: /// An object that represents the value of the removed cache entry that was specified by the key, or null if the specified entry was not found.
183: /// </returns>
184: public override object Remove(string key, string regionName = null)
185: {
186: var data = this.Get(key, regionName);
187: if (data != null)
188: {
189: this._cacheFactory.GetDefaultCache().Remove(GetKey(key, regionName));
190: return data;
191: }
192:
193: return null;
194: }
195:
196: /// <summary>
197: /// When overridden in a derived class, inserts a cache entry into the cache.
198: /// </summary>
199: /// <param name="key">A unique identifier for the cache entry.</param>
200: /// <param name="value">The object to insert.</param>
201: /// <param name="policy">An object that contains eviction details for the cache entry. This object provides more options for eviction than a simple absolute expiration.</param>
202: /// <param name="regionName">Optional. A named region in the cache to which the cache entry can be added, if regions are implemented. The default value for the optional parameter is null.</param>
203: public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
204: {
205: this.Set(new CacheItem(key, value, regionName), policy);
206: }
207:
208: /// <summary>
209: /// When overridden in a derived class, inserts a cache entry into the cache, specifying time-based expiration details.
210: /// </summary>
211: /// <param name="key">A unique identifier for the cache entry.</param>
212: /// <param name="value">The object to insert.</param>
213: /// <param name="absoluteExpiration">The fixed date and time at which the cache entry will expire.</param>
214: /// <param name="regionName">Optional. A named region in the cache to which the cache entry can be added, if regions are implemented. The default value for the optional parameter is null.</param>
215: public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
216: {
217: this.Set(new CacheItem(key, value, regionName), new CacheItemPolicy { AbsoluteExpiration = absoluteExpiration });
218: }
219:
220: /// <summary>
221: /// When overridden in a derived class, inserts the cache entry into the cache as a <see cref="T:System.Runtime.Caching.CacheItem"/> instance, specifying information about how the entry will be evicted.
222: /// </summary>
223: /// <param name="item">The cache item to add.</param>
224: /// <param name="policy">An object that contains eviction details for the cache entry. This object provides more options for eviction than a simple absolute expiration.</param>
225: public override void Set(CacheItem item, CacheItemPolicy policy)
226: {
227: if (item.Value == null) return;
228:
229: try
230: {
231: TimeSpan timeout = TimeSpan.FromMinutes(1);
232: if (policy != null)
233: {
234: if (policy.SlidingExpiration != TimeSpan.Zero)
235: {
236: timeout = policy.SlidingExpiration;
237: }
238: else
239: {
240: timeout = policy.AbsoluteExpiration - DateTime.UtcNow;
241: }
242: }
243:
244: if (policy == null)
245: this._cacheFactory.GetDefaultCache().Put(GetKey(item.Key, item.RegionName), item.Value);
246: else
247: this._cacheFactory.GetDefaultCache().Put(GetKey(item.Key, item.RegionName), item.Value, timeout);
248: }
249: catch (DataCacheException ex)
250: {
251: if (ex.ErrorCode == DataCacheErrorCode.RetryLater)
252: {
253: // temporal failure, ignore and continue
254: return;
255: }
256:
257: throw;
258: }
259: }
260:
261: public override long GetCount(string regionName = null)
262: {
263: throw new NotSupportedException();
264: }
265:
266: protected override IEnumerator<System.Collections.Generic.KeyValuePair<string, object>> GetEnumerator()
267: {
268: throw new NotSupportedException();
269: }
270:
271: public override IDictionary<string, object> GetValues(System.Collections.Generic.IEnumerable<string> keys, string regionName = null)
272: {
273: throw new NotSupportedException();
274: }
275:
276: public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(System.Collections.Generic.IEnumerable<string> keys, string regionName = null)
277: {
278: throw new NotSupportedException();
279: }
280:
281: private static string GetKey(string key, string regionName)
282: {
283: if (string.IsNullOrWhiteSpace(regionName))
284: {
285: return key;
286: }
287: else
288: {
289: return string.Format(CultureInfo.InvariantCulture, RegionKeyTemplate, key, regionName);
290: }
291: }
292:
293: public void Dispose()
294: {
295: Dispose(true);
296: GC.SuppressFinalize(this);
297: }
298:
299: // The bulk of the clean-up code is implemented in Dispose(bool)
300: protected virtual void Dispose(bool disposing)
301: {
302: if (disposing)
303: {
304: // free managed resources
305: if (_cacheFactory != null)
306: {
307: _cacheFactory.Dispose();
308: _cacheFactory = null;
309: }
310: }
311: }
312: }