1 //------------------------------------------------------------------------------
2 // <copyright file="cache.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
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
;
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
;
35 /// <para>Represents the method that will handle the <see langword='onRemoveCallback'/>
36 /// event of a System.Web.Caching.Cache instance.</para>
38 public delegate void CacheItemRemovedCallback(
39 string key
, object value, CacheItemRemovedReason reason
);
42 /// <para>Represents the method that will handle the <see langword='onUpdateCallback'/>
43 /// event of a System.Web.Caching.Cache instance.</para>
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
);
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
60 public enum CacheItemPriority
{
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
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>
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>
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
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>
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>
102 /// <para>The default value is Normal.</para>
109 /// <para>Specifies the reason that a cached item was removed.</para>
111 public enum CacheItemRemovedReason
{
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>
119 /// <para>The item was removed from the cache because it expired. </para>
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>
130 /// <para>The item was removed from the cache because a file or key dependency was
137 /// <para>Specifies the reason why a cached item needs to be updated.</para>
139 [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue",
140 Justification
= "This enum should mirror CacheItemRemovedReason enum in design")]
141 public enum CacheItemUpdateReason
{
144 /// <para>The item needs to be updated because it expired. </para>
149 /// <para>The item needs to be updated because a file or key dependency was
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>
164 // - The Cache object contains a ICacheStore object and wraps it for public consumption.
166 public sealed class Cache
: IEnumerable
{
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>
174 public static readonly DateTime NoAbsoluteExpiration
= DateTime
.MaxValue
;
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>
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
);
190 /// <para>This constructor is for internal use only, and was accidentally made public - do not use.</para>
192 [SecurityPermission(SecurityAction
.Demand
, Unrestricted
=true)]
197 // internal ctor used by CacheCommon that avoids the demand for UnmanagedCode.
199 internal Cache(int dummy
) {
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>
210 return Convert
.ToInt32(ObjectCache
.ItemCount
);
215 internal CacheStoreProvider
GetInternalCache(bool createIfDoesNotExist
) {
216 if (_internalCache
== null && createIfDoesNotExist
) {
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
);
228 if (_objectCache
is AspNetCache
) {
229 _internalCache
= new AspNetCache((AspNetCache
)_objectCache
, isPublic
: false);
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
) {
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
);
258 if (_internalCache
is AspNetCache
) {
259 _objectCache
= new AspNetCache((AspNetCache
)_internalCache
, isPublic
: true);
262 _objectCache
= new AspNetCache(isPublic
: true);
264 _objectCache
.Initialize(null, new NameValueCollection());
274 /// <para>Provides access to the cache store used by ASP.Net internals.</para>
276 internal CacheStoreProvider InternalCache
{
277 get { return GetInternalCache(createIfDoesNotExist: true); }
281 /// <para>Provides access to the cache store that backs HttpRuntime.Cache.</para>
283 internal CacheStoreProvider ObjectCache
{
284 get { return GetObjectCache(createIfDoesNotExist: true); }
288 IEnumerator IEnumerable
.GetEnumerator() {
289 return ObjectCache
.GetEnumerator();
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>
298 public IDictionaryEnumerator
GetEnumerator() {
299 return ObjectCache
.GetEnumerator();
304 /// <para>Gets or sets an item in the cache.</para>
306 public object this[string key
] {
316 private class SentinelEntry
{
318 private CacheDependency _expensiveObjectDependency
;
319 private CacheItemUpdateCallback _cacheItemUpdateCallback
;
321 public SentinelEntry(string key
, CacheDependency expensiveObjectDependency
, CacheItemUpdateCallback callback
) {
323 _expensiveObjectDependency
= expensiveObjectDependency
;
324 _cacheItemUpdateCallback
= callback
;
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
;
344 case CacheItemRemovedReason
.Expired
:
345 updateReason
= CacheItemUpdateReason
.Expired
;
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.
356 case CacheItemRemovedReason
.Underused
:
357 Debug
.Fail("Reason should never be CacheItemRemovedReason.Underused since the entry was inserted as NotRemovable.");
360 // do nothing if reason is Removed
364 CacheDependency cacheDependency
;
365 DateTime absoluteExpiration
;
366 TimeSpan slidingExpiration
;
367 object expensiveObject
;
368 CacheItemUpdateCallback callback
= entry
.CacheItemUpdateCallback
;
369 // invoke update callback
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
);
378 HttpRuntime
.Cache
.Remove(entry
.Key
);
381 catch (Exception e
) {
382 HttpRuntime
.Cache
.Remove(entry
.Key
);
384 WebBaseEvent
.RaiseRuntimeError(e
, value);
393 /// <para>Retrieves an item from the cache.</para>
395 public object Get(string key
) {
396 return ObjectCache
.Get(key
);
401 /// <para>Inserts an item into the Cache with default values.</para>
403 public void Insert(string key
, object value) {
404 ObjectCache
.Insert(key
, value, options
: null);
409 /// <para>Inserts an object into the System.Web.Caching.Cache that has file or key
410 /// dependencies.</para>
412 public void Insert(string key
, object value, CacheDependency dependencies
) {
413 ObjectCache
.Insert(key
, value, new CacheInsertOptions() { Dependencies = dependencies }
);
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>
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
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
,
445 OnRemovedCallback
= onRemoveCallback
449 // DevDiv Bugs 162763:
450 // Add a an event that fires *before* an item is evicted from the ASP.NET Cache
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
;
476 AggregateCacheDependency deps
= new AggregateCacheDependency();
477 deps
.Add(dependencies
, expensiveObjectDep
);
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
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
,
509 OnRemovedCallback
= onRemoveCallback
515 /// <para>Removes the specified item from the cache. </para>
517 public object Remove(string key
) {
518 return ObjectCache
.Remove(key
, CacheItemRemovedReason
.Removed
);
521 public long EffectivePrivateBytesLimit
{
523 return AspNetMemoryMonitor
.ProcessPrivateBytesLimit
;
527 public long EffectivePercentagePhysicalMemoryLimit
{
529 return AspNetMemoryMonitor
.PhysicalMemoryPercentageLimit
;