[bcl] Updates referencesource to 4.7.1
[mono-project.git] / mcs / class / referencesource / System.Web / Cache / cache.cs
blob1bb9b496a67fa360cb8a5d8c51d80fcfc2e76f96
1 //------------------------------------------------------------------------------
2 // <copyright file="cache.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
7 /*
8 * Cache class
10 * Copyright (c) 1999 Microsoft Corporation
13 namespace System.Web.Caching {
14 using System.Collections;
15 using System.Collections.Specialized;
16 using System.Configuration;
17 using System.Configuration.Provider;
18 using System.Diagnostics;
19 using System.Diagnostics.CodeAnalysis;
20 using System.Reflection;
21 using System.Runtime.InteropServices;
22 using System.Threading;
23 using System.Web.Util;
24 using System.Web;
25 using Microsoft.Win32;
26 using System.Security.Permissions;
27 using System.Globalization;
28 using System.Web.Configuration;
29 using System.Web.Hosting;
30 using System.Web.Management;
31 using Debug = System.Web.Util.Debug;
34 /// <devdoc>
35 /// <para>Represents the method that will handle the <see langword='onRemoveCallback'/>
36 /// event of a System.Web.Caching.Cache instance.</para>
37 /// </devdoc>
38 public delegate void CacheItemRemovedCallback(
39 string key, object value, CacheItemRemovedReason reason);
41 /// <devdoc>
42 /// <para>Represents the method that will handle the <see langword='onUpdateCallback'/>
43 /// event of a System.Web.Caching.Cache instance.</para>
44 /// </devdoc>
45 [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters",
46 Justification="Shipped this way in NetFx 2.0 SP2")]
47 public delegate void CacheItemUpdateCallback(
48 string key, CacheItemUpdateReason reason,
49 out object expensiveObject, out CacheDependency dependency, out DateTime absoluteExpiration, out TimeSpan slidingExpiration);
51 /// <devdoc>
52 /// <para> Specifies the relative priority of items stored in the System.Web.Caching.Cache. When the Web
53 /// server runs low on memory, the Cache selectively purges items to free system
54 /// memory. Items with higher priorities are less likely to be removed from the
55 /// cache when the server is under load. Web
56 /// applications can use these
57 /// values to prioritize cached items relative to one another. The default is
58 /// normal.</para>
59 /// </devdoc>
60 public enum CacheItemPriority {
62 /// <devdoc>
63 /// <para> The cahce items with this priority level will be the first
64 /// to be removed when the server frees system memory by deleting items from the
65 /// cache.</para>
66 /// </devdoc>
67 Low = 1,
69 /// <devdoc>
70 /// <para> The cache items with this priority level
71 /// are in the second group to be removed when the server frees system memory by
72 /// deleting items from the cache. </para>
73 /// </devdoc>
74 BelowNormal,
76 /// <devdoc>
77 /// <para> The cache items with this priority level are in
78 /// the third group to be removed when the server frees system memory by deleting items from the cache. This is the default. </para>
79 /// </devdoc>
80 Normal,
82 /// <devdoc>
83 /// <para> The cache items with this priority level are in the
84 /// fourth group to be removed when the server frees system memory by deleting items from the
85 /// cache. </para>
86 /// </devdoc>
87 AboveNormal,
89 /// <devdoc>
90 /// <para>The cache items with this priority level are in the fifth group to be removed
91 /// when the server frees system memory by deleting items from the cache. </para>
92 /// </devdoc>
93 High,
95 /// <devdoc>
96 /// <para>The cache items with this priority level will not be removed when the server
97 /// frees system memory by deleting items from the cache. </para>
98 /// </devdoc>
99 NotRemovable,
101 /// <devdoc>
102 /// <para>The default value is Normal.</para>
103 /// </devdoc>
104 Default = Normal
108 /// <devdoc>
109 /// <para>Specifies the reason that a cached item was removed.</para>
110 /// </devdoc>
111 public enum CacheItemRemovedReason {
113 /// <devdoc>
114 /// <para>The item was removed from the cache by the 'System.Web.Caching.Cache.Remove' method, or by an System.Web.Caching.Cache.Insert method call specifying the same key.</para>
115 /// </devdoc>
116 Removed = 1,
118 /// <devdoc>
119 /// <para>The item was removed from the cache because it expired. </para>
120 /// </devdoc>
121 Expired,
123 /// <devdoc>
124 /// <para>The item was removed from the cache because the value in the hitInterval
125 /// parameter was not met, or because the system removed it to free memory.</para>
126 /// </devdoc>
127 Underused,
129 /// <devdoc>
130 /// <para>The item was removed from the cache because a file or key dependency was
131 /// changed.</para>
132 /// </devdoc>
133 DependencyChanged
136 /// <devdoc>
137 /// <para>Specifies the reason why a cached item needs to be updated.</para>
138 /// </devdoc>
139 [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue",
140 Justification = "This enum should mirror CacheItemRemovedReason enum in design")]
141 public enum CacheItemUpdateReason {
143 /// <devdoc>
144 /// <para>The item needs to be updated because it expired. </para>
145 /// </devdoc>
146 Expired = 1,
148 /// <devdoc>
149 /// <para>The item needs to be updated because a file or key dependency was
150 /// changed.</para>
151 /// </devdoc>
152 DependencyChanged
155 /// <devdoc>
156 /// <para>Implements the cache for a Web application. There is only one instance of
157 /// this class per application domain, and it remains valid only as long as the
158 /// application domain remains active. Information about an instance of this class
159 /// is available through the <see langword='Cache'/> property of the System.Web.HttpContext.</para>
160 /// </devdoc>
163 // Extra notes:
164 // - The Cache object contains a ICacheStore object and wraps it for public consumption.
166 public sealed class Cache : IEnumerable {
168 /// <devdoc>
169 /// <para>Sets the absolute expiration policy to, in essence,
170 /// never. When set, this field is equal to the the System.DateTime.MaxValue , which is a constant
171 /// representing the largest possible <see langword='DateTime'/> value. The maximum date and
172 /// time value is equivilant to "12/31/9999 11:59:59 PM". This field is read-only.</para>
173 /// </devdoc>
174 public static readonly DateTime NoAbsoluteExpiration = DateTime.MaxValue;
177 /// <devdoc>
178 /// <para>Sets the amount of time for sliding cache expirations to
179 /// zero. When set, this field is equal to the System.TimeSpan.Zero field, which is a constant value of
180 /// zero. This field is read-only.</para>
181 /// </devdoc>
182 public static readonly TimeSpan NoSlidingExpiration = TimeSpan.Zero;
184 static CacheStoreProvider _objectCache = null;
185 static CacheStoreProvider _internalCache = null;
186 static CacheItemRemovedCallback s_sentinelRemovedCallback = new CacheItemRemovedCallback(SentinelEntry.OnCacheItemRemovedCallback);
188 /// <internalonly/>
189 /// <devdoc>
190 /// <para>This constructor is for internal use only, and was accidentally made public - do not use.</para>
191 /// </devdoc>
192 [SecurityPermission(SecurityAction.Demand, Unrestricted=true)]
193 public Cache() {
197 // internal ctor used by CacheCommon that avoids the demand for UnmanagedCode.
199 internal Cache(int dummy) {
203 /// <devdoc>
204 /// <para>Gets the number of items stored in the cache. This value can be useful when
205 /// monitoring your application's performance or when using the ASP.NET tracing
206 /// functionality.</para>
207 /// </devdoc>
208 public int Count {
209 get {
210 return Convert.ToInt32(ObjectCache.ItemCount);
215 internal CacheStoreProvider GetInternalCache(bool createIfDoesNotExist) {
216 if (_internalCache == null && createIfDoesNotExist) {
217 lock (this) {
218 if (_internalCache == null) {
219 NameValueCollection cacheProviderSettings = HostingEnvironment.CacheStoreProviderSettings;
221 if (cacheProviderSettings != null) {
222 string providerName = (string)cacheProviderSettings["name"]; // Grab this now, as InstantiateProvider will remove it from settings
223 cacheProviderSettings["isPublic"] = "false";
224 _internalCache = (CacheStoreProvider)ProvidersHelper.InstantiateProvider(cacheProviderSettings, typeof(CacheStoreProvider));
225 _internalCache.Initialize(providerName, cacheProviderSettings);
227 else {
228 if (_objectCache is AspNetCache) {
229 _internalCache = new AspNetCache((AspNetCache)_objectCache, isPublic: false);
231 else {
232 _internalCache = new AspNetCache(isPublic: false);
234 _internalCache.Initialize(null, new NameValueCollection());
240 return _internalCache;
243 [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
244 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control this method's callers.")]
245 internal CacheStoreProvider GetObjectCache(bool createIfDoesNotExist) {
246 if (_objectCache == null && createIfDoesNotExist) {
247 lock (this) {
248 if (_objectCache == null) {
249 NameValueCollection cacheProviderSettings = HostingEnvironment.CacheStoreProviderSettings;
251 if (cacheProviderSettings != null) {
252 string providerName = (string)cacheProviderSettings["name"]; // Grab this now, as InstantiateProvider will remove it from settings
253 cacheProviderSettings["isPublic"] = "true";
254 _objectCache = (CacheStoreProvider)ProvidersHelper.InstantiateProvider(cacheProviderSettings, typeof(CacheStoreProvider));
255 _objectCache.Initialize(providerName, cacheProviderSettings);
257 else {
258 if (_internalCache is AspNetCache) {
259 _objectCache = new AspNetCache((AspNetCache)_internalCache, isPublic: true);
261 else {
262 _objectCache = new AspNetCache(isPublic: true);
264 _objectCache.Initialize(null, new NameValueCollection());
270 return _objectCache;
273 /// <devdoc>
274 /// <para>Provides access to the cache store used by ASP.Net internals.</para>
275 /// </devdoc>
276 internal CacheStoreProvider InternalCache {
277 get { return GetInternalCache(createIfDoesNotExist: true); }
280 /// <devdoc>
281 /// <para>Provides access to the cache store that backs HttpRuntime.Cache.</para>
282 /// </devdoc>
283 internal CacheStoreProvider ObjectCache {
284 get { return GetObjectCache(createIfDoesNotExist: true); }
287 /// <internalonly/>
288 IEnumerator IEnumerable.GetEnumerator() {
289 return ObjectCache.GetEnumerator();
293 /// <devdoc>
294 /// <para>Returns a dictionary enumerator used for iterating through the key/value
295 /// pairs contained in the cache. Items can be added to or removed from the cache
296 /// while this method is enumerating through the cache items.</para>
297 /// </devdoc>
298 public IDictionaryEnumerator GetEnumerator() {
299 return ObjectCache.GetEnumerator();
303 /// <devdoc>
304 /// <para>Gets or sets an item in the cache.</para>
305 /// </devdoc>
306 public object this[string key] {
307 get {
308 return Get(key);
311 set {
312 Insert(key, value);
316 private class SentinelEntry {
317 private string _key;
318 private CacheDependency _expensiveObjectDependency;
319 private CacheItemUpdateCallback _cacheItemUpdateCallback;
321 public SentinelEntry(string key, CacheDependency expensiveObjectDependency, CacheItemUpdateCallback callback) {
322 _key = key;
323 _expensiveObjectDependency = expensiveObjectDependency;
324 _cacheItemUpdateCallback = callback;
327 public string Key {
328 get { return _key; }
331 public CacheDependency ExpensiveObjectDependency {
332 get { return _expensiveObjectDependency; }
335 public CacheItemUpdateCallback CacheItemUpdateCallback {
336 get { return _cacheItemUpdateCallback; }
339 public static void OnCacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason) {
340 CacheItemUpdateReason updateReason;
341 SentinelEntry entry = value as SentinelEntry;
343 switch (reason) {
344 case CacheItemRemovedReason.Expired:
345 updateReason = CacheItemUpdateReason.Expired;
346 break;
347 case CacheItemRemovedReason.DependencyChanged:
348 updateReason = CacheItemUpdateReason.DependencyChanged;
349 if (entry.ExpensiveObjectDependency.HasChanged) {
350 // If the expensiveObject has been removed explicitly by Cache.Remove,
351 // return from the SentinelEntry removed callback
352 // thus effectively removing the SentinelEntry from the cache.
353 return;
355 break;
356 case CacheItemRemovedReason.Underused:
357 Debug.Fail("Reason should never be CacheItemRemovedReason.Underused since the entry was inserted as NotRemovable.");
358 return;
359 default:
360 // do nothing if reason is Removed
361 return;
364 CacheDependency cacheDependency;
365 DateTime absoluteExpiration;
366 TimeSpan slidingExpiration;
367 object expensiveObject;
368 CacheItemUpdateCallback callback = entry.CacheItemUpdateCallback;
369 // invoke update callback
370 try {
371 callback(entry.Key, updateReason, out expensiveObject, out cacheDependency, out absoluteExpiration, out slidingExpiration);
372 // Dev10 861163 - Only update the "expensive" object if the user returns a new object and the
373 // cache dependency hasn't changed. (Inserting with a cache dependency that has already changed will cause recursion.)
374 if (expensiveObject != null && (cacheDependency == null || !cacheDependency.HasChanged)) {
375 HttpRuntime.Cache.Insert(entry.Key, expensiveObject, cacheDependency, absoluteExpiration, slidingExpiration, entry.CacheItemUpdateCallback);
377 else {
378 HttpRuntime.Cache.Remove(entry.Key);
381 catch (Exception e) {
382 HttpRuntime.Cache.Remove(entry.Key);
383 try {
384 WebBaseEvent.RaiseRuntimeError(e, value);
386 catch {
392 /// <devdoc>
393 /// <para>Retrieves an item from the cache.</para>
394 /// </devdoc>
395 public object Get(string key) {
396 return ObjectCache.Get(key);
400 /// <devdoc>
401 /// <para>Inserts an item into the Cache with default values.</para>
402 /// </devdoc>
403 public void Insert(string key, object value) {
404 ObjectCache.Insert(key, value, options: null);
408 /// <devdoc>
409 /// <para>Inserts an object into the System.Web.Caching.Cache that has file or key
410 /// dependencies.</para>
411 /// </devdoc>
412 public void Insert(string key, object value, CacheDependency dependencies) {
413 ObjectCache.Insert(key, value, new CacheInsertOptions() { Dependencies = dependencies });
417 /// <devdoc>
418 /// <para>Inserts an object into the System.Web.Caching.Cache that has file or key dependencies and
419 /// expires at the value set in the <paramref name="absoluteExpiration"/> parameter.</para>
420 /// </devdoc>
421 public void Insert(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration) {
422 DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
423 ObjectCache.Insert(key, value, new CacheInsertOptions() {
424 Dependencies = dependencies,
425 AbsoluteExpiration = utcAbsoluteExpiration,
426 SlidingExpiration = slidingExpiration
430 public void Insert(
431 string key,
432 object value,
433 CacheDependency dependencies,
434 DateTime absoluteExpiration,
435 TimeSpan slidingExpiration,
436 CacheItemPriority priority,
437 CacheItemRemovedCallback onRemoveCallback) {
439 DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
440 ObjectCache.Insert(key, value, new CacheInsertOptions() {
441 Dependencies = dependencies,
442 AbsoluteExpiration = utcAbsoluteExpiration,
443 SlidingExpiration = slidingExpiration,
444 Priority = priority,
445 OnRemovedCallback = onRemoveCallback
449 // DevDiv Bugs 162763:
450 // Add a an event that fires *before* an item is evicted from the ASP.NET Cache
451 public void Insert(
452 string key,
453 object value,
454 CacheDependency dependencies,
455 DateTime absoluteExpiration,
456 TimeSpan slidingExpiration,
457 CacheItemUpdateCallback onUpdateCallback) {
459 if (dependencies == null && absoluteExpiration == Cache.NoAbsoluteExpiration && slidingExpiration == Cache.NoSlidingExpiration) {
460 throw new ArgumentException(SR.GetString(SR.Invalid_Parameters_To_Insert));
462 if (onUpdateCallback == null) {
463 throw new ArgumentNullException("onUpdateCallback");
465 DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
466 // Insert updatable cache entry
467 ObjectCache.Insert(key, value, new CacheInsertOptions() { Priority = CacheItemPriority.NotRemovable });
469 // Ensure the sentinel depends on its updatable entry
470 string[] cacheKeys = { key };
471 CacheDependency expensiveObjectDep = new CacheDependency(null, cacheKeys);
472 if (dependencies == null) {
473 dependencies = expensiveObjectDep;
475 else {
476 AggregateCacheDependency deps = new AggregateCacheDependency();
477 deps.Add(dependencies, expensiveObjectDep);
478 dependencies = deps;
480 // Insert sentinel entry for the updatable cache entry
481 HttpRuntime.Cache.InternalCache.Insert(
482 CacheInternal.PrefixValidationSentinel + key,
483 new SentinelEntry(key, expensiveObjectDep, onUpdateCallback),
484 new CacheInsertOptions() {
485 Dependencies = dependencies,
486 AbsoluteExpiration = utcAbsoluteExpiration,
487 SlidingExpiration = slidingExpiration,
488 Priority = CacheItemPriority.NotRemovable,
489 OnRemovedCallback = Cache.s_sentinelRemovedCallback
494 public object Add(
495 string key,
496 object value,
497 CacheDependency dependencies,
498 DateTime absoluteExpiration,
499 TimeSpan slidingExpiration,
500 CacheItemPriority priority,
501 CacheItemRemovedCallback onRemoveCallback) {
503 DateTime utcAbsoluteExpiration = DateTimeUtil.ConvertToUniversalTime(absoluteExpiration);
504 return ObjectCache.Add(key, value, new CacheInsertOptions() {
505 Dependencies = dependencies,
506 AbsoluteExpiration = utcAbsoluteExpiration,
507 SlidingExpiration = slidingExpiration,
508 Priority = priority,
509 OnRemovedCallback = onRemoveCallback
514 /// <devdoc>
515 /// <para>Removes the specified item from the cache. </para>
516 /// </devdoc>
517 public object Remove(string key) {
518 return ObjectCache.Remove(key, CacheItemRemovedReason.Removed);
521 public long EffectivePrivateBytesLimit {
522 get {
523 return AspNetMemoryMonitor.ProcessPrivateBytesLimit;
527 public long EffectivePercentagePhysicalMemoryLimit {
528 get {
529 return AspNetMemoryMonitor.PhysicalMemoryPercentageLimit;