1
// <copyright file="MemoryCache.cs" company="Microsoft">
2 // Copyright (c) 2009 Microsoft Corporation. All rights reserved.
5 using System
.Runtime
.Caching
.Configuration
;
6 using System
.Runtime
.Caching
.Resources
;
7 using System
.Collections
;
8 using System
.Collections
.Generic
;
9 using System
.Collections
.Specialized
;
10 using System
.Collections
.ObjectModel
;
11 using System
.Configuration
;
12 using System
.Diagnostics
.CodeAnalysis
;
13 using System
.Security
;
14 using System
.Security
.Permissions
;
15 using System
.Threading
;
17 namespace System
.Runtime
.Caching
{
18 [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification
= "The class represents a type of cache")]
19 public class MemoryCache
: ObjectCache
, IEnumerable
, IDisposable
{
20 private const DefaultCacheCapabilities CAPABILITIES
= DefaultCacheCapabilities
.InMemoryProvider
21 | DefaultCacheCapabilities
.CacheEntryChangeMonitors
22 | DefaultCacheCapabilities
.AbsoluteExpirations
23 | DefaultCacheCapabilities
.SlidingExpirations
24 | DefaultCacheCapabilities
.CacheEntryUpdateCallback
25 | DefaultCacheCapabilities
.CacheEntryRemovedCallback
;
26 private static readonly TimeSpan OneYear
= new TimeSpan(365, 0, 0, 0);
27 private static object s_initLock
= new object();
28 private static MemoryCache s_defaultCache
;
29 private static CacheEntryRemovedCallback s_sentinelRemovedCallback
= new CacheEntryRemovedCallback(SentinelEntry
.OnCacheEntryRemovedCallback
);
30 private GCHandleRef
<MemoryCacheStore
>[] _storeRefs
;
31 private int _storeCount
;
32 private int _disposed
;
33 private MemoryCacheStatistics _stats
;
35 private PerfCounters _perfCounters
;
36 private bool _configLess
;
37 private bool _useMemoryCacheManager
= true;
38 EventHandler _onAppDomainUnload
;
39 UnhandledExceptionEventHandler _onUnhandledException
;
41 private bool IsDisposed { get { return (_disposed == 1); }
}
42 internal bool ConfigLess { get { return _configLess; }
}
44 private class SentinelEntry
{
46 private ChangeMonitor _expensiveObjectDependency
;
47 private CacheEntryUpdateCallback _updateCallback
;
49 internal SentinelEntry(string key
, ChangeMonitor expensiveObjectDependency
, CacheEntryUpdateCallback callback
) {
51 _expensiveObjectDependency
= expensiveObjectDependency
;
52 _updateCallback
= callback
;
59 internal ChangeMonitor ExpensiveObjectDependency
{
60 get { return _expensiveObjectDependency; }
63 internal CacheEntryUpdateCallback CacheEntryUpdateCallback
{
64 get { return _updateCallback; }
67 private static bool IsPolicyValid(CacheItemPolicy policy
) {
71 // see if any change monitors have changed
72 bool hasChanged
= false;
73 Collection
<ChangeMonitor
> changeMonitors
= policy
.ChangeMonitors
;
74 if (changeMonitors
!= null) {
75 foreach (ChangeMonitor monitor
in changeMonitors
) {
76 if (monitor
!= null && monitor
.HasChanged
) {
82 // if the monitors haven't changed yet and we have an update callback
83 // then the policy is valid
84 if (!hasChanged
&& policy
.UpdateCallback
!= null) {
87 // if the monitors have changed we need to dispose them
89 foreach (ChangeMonitor monitor
in changeMonitors
) {
90 if (monitor
!= null) {
98 internal static void OnCacheEntryRemovedCallback(CacheEntryRemovedArguments arguments
) {
99 MemoryCache cache
= arguments
.Source
as MemoryCache
;
100 SentinelEntry entry
= arguments
.CacheItem
.Value
as SentinelEntry
;
101 CacheEntryRemovedReason reason
= arguments
.RemovedReason
;
103 case CacheEntryRemovedReason
.Expired
:
105 case CacheEntryRemovedReason
.ChangeMonitorChanged
:
106 if (entry
.ExpensiveObjectDependency
.HasChanged
) {
107 // If the expensiveObject has been removed explicitly by Cache.Remove,
108 // return from the SentinelEntry removed callback
109 // thus effectively removing the SentinelEntry from the cache.
113 case CacheEntryRemovedReason
.Evicted
:
114 Dbg
.Fail("Reason should never be CacheEntryRemovedReason.Evicted since the entry was inserted as NotRemovable.");
117 // do nothing if reason is Removed or CacheSpecificEviction
121 // invoke update callback
123 CacheEntryUpdateArguments args
= new CacheEntryUpdateArguments(cache
, reason
, entry
.Key
, null);
124 entry
.CacheEntryUpdateCallback(args
);
125 Object expensiveObject
= (args
.UpdatedCacheItem
!= null) ? args
.UpdatedCacheItem
.Value
: null;
126 CacheItemPolicy policy
= args
.UpdatedCacheItemPolicy
;
127 // Dev10 861163 - Only update the "expensive" object if the user returns a new object,
128 // a policy with update callback, and the change monitors haven't changed. (Inserting
129 // with change monitors that have already changed will cause recursion.)
130 if (expensiveObject
!= null && IsPolicyValid(policy
)) {
131 cache
.Set(entry
.Key
, expensiveObject
, policy
);
134 cache
.Remove(entry
.Key
);
138 cache
.Remove(entry
.Key
);
139 // Review: What should we do with this exception?
145 // private and internal
147 internal MemoryCacheStore
GetStore(MemoryCacheKey cacheKey
) {
148 // Dev10 865907: Math.Abs throws OverflowException for Int32.MinValue
149 int hashCode
= cacheKey
.Hash
;
151 hashCode
= (hashCode
== Int32
.MinValue
) ? 0 : -hashCode
;
153 int idx
= hashCode
% _storeCount
;
154 return _storeRefs
[idx
].Target
;
157 internal object[] AllSRefTargets
{
159 var allStores
= new MemoryCacheStore
[_storeCount
];
160 for (int i
= 0; i
< _storeCount
; i
++) {
161 allStores
[i
] = _storeRefs
[i
].Target
;
167 [SecuritySafeCritical
]
168 [PermissionSet(SecurityAction
.Assert
, Unrestricted
= true)]
169 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification
= "Grandfathered suppression from original caching code checkin")]
170 private void InitDisposableMembers(NameValueCollection config
) {
174 _perfCounters
= new PerfCounters(_name
);
177 // ignore exceptions from perf counters
179 for (int i
= 0; i
< _storeCount
; i
++) {
180 _storeRefs
[i
] = new GCHandleRef
<MemoryCacheStore
> (new MemoryCacheStore(this, _perfCounters
));
182 _stats
= new MemoryCacheStatistics(this, config
);
183 AppDomain appDomain
= Thread
.GetDomain();
184 EventHandler onAppDomainUnload
= new EventHandler(OnAppDomainUnload
);
185 appDomain
.DomainUnload
+= onAppDomainUnload
;
186 _onAppDomainUnload
= onAppDomainUnload
;
187 UnhandledExceptionEventHandler onUnhandledException
= new UnhandledExceptionEventHandler(OnUnhandledException
);
188 appDomain
.UnhandledException
+= onUnhandledException
;
189 _onUnhandledException
= onUnhandledException
;
199 private void OnAppDomainUnload(Object unusedObject
, EventArgs unusedEventArgs
) {
203 private void OnUnhandledException(Object sender
, UnhandledExceptionEventArgs eventArgs
) {
204 // if the CLR is terminating, dispose the cache.
205 // This will dispose the perf counters (see Dev10 680819).
206 if (eventArgs
.IsTerminating
) {
211 private void ValidatePolicy(CacheItemPolicy policy
) {
212 if (policy
.AbsoluteExpiration
!= ObjectCache
.InfiniteAbsoluteExpiration
213 && policy
.SlidingExpiration
!= ObjectCache
.NoSlidingExpiration
) {
214 throw new ArgumentException(R
.Invalid_expiration_combination
, "policy");
216 if (policy
.SlidingExpiration
< ObjectCache
.NoSlidingExpiration
|| OneYear
< policy
.SlidingExpiration
) {
217 throw new ArgumentOutOfRangeException("policy", RH
.Format(R
.Argument_out_of_range
, "SlidingExpiration", ObjectCache
.NoSlidingExpiration
, OneYear
));
219 if (policy
.RemovedCallback
!= null
220 && policy
.UpdateCallback
!= null) {
221 throw new ArgumentException(R
.Invalid_callback_combination
, "policy");
223 if (policy
.Priority
!= CacheItemPriority
.Default
&& policy
.Priority
!= CacheItemPriority
.NotRemovable
) {
224 throw new ArgumentOutOfRangeException("policy", RH
.Format(R
.Argument_out_of_range
, "Priority", CacheItemPriority
.Default
, CacheItemPriority
.NotRemovable
));
230 // Amount of memory that can be used before
231 // the cache begins to forcibly remove items.
232 public long CacheMemoryLimit
{
234 return _stats
.CacheMemoryLimit
;
238 public static MemoryCache Default
{
240 if (s_defaultCache
== null) {
242 if (s_defaultCache
== null) {
243 s_defaultCache
= new MemoryCache();
247 return s_defaultCache
;
251 public override DefaultCacheCapabilities DefaultCacheCapabilities
{
257 public override string Name
259 get { return _name; }
262 internal bool UseMemoryCacheManager
{
263 get { return _useMemoryCacheManager; }
266 // Percentage of physical memory that can be used before
267 // the cache begins to forcibly remove items.
268 public long PhysicalMemoryLimit
{
270 return _stats
.PhysicalMemoryLimit
;
274 // The maximum interval of time afterwhich the cache
275 // will update its memory statistics.
276 public TimeSpan PollingInterval
{
278 return _stats
.PollingInterval
;
282 // Only used for Default MemoryCache
283 private MemoryCache() {
288 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
= "This is assembly is a special case approved by the NetFx API review board")]
289 public MemoryCache(string name
, NameValueCollection config
= null) {
291 throw new ArgumentNullException("name");
293 if (name
== String
.Empty
) {
294 throw new ArgumentException(R
.Empty_string_invalid
, "name");
296 if (String
.Equals(name
, "default", StringComparison
.OrdinalIgnoreCase
)) {
297 throw new ArgumentException(R
.Default_is_reserved
, "name");
303 // ignoreConfigSection is used when redirecting ASP.NET cache into the MemoryCache. This avoids infinite recursion
304 // due to the fact that the (ASP.NET) config system uses the cache, and the cache uses the
305 // config system. This method could be made public, perhaps with CAS to prevent partial trust callers.
306 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
= "This is assembly is a special case approved by the NetFx API review board")]
307 public MemoryCache(string name
, NameValueCollection config
, bool ignoreConfigSection
) {
309 throw new ArgumentNullException("name");
311 if (name
== String
.Empty
) {
312 throw new ArgumentException(R
.Empty_string_invalid
, "name");
314 if (String
.Equals(name
, "default", StringComparison
.OrdinalIgnoreCase
)) {
315 throw new ArgumentException(R
.Default_is_reserved
, "name");
318 _configLess
= ignoreConfigSection
;
323 private void Init(NameValueCollection config
) {
324 _storeCount
= Environment
.ProcessorCount
;
325 if (config
!= null) {
327 if (config
["__MonoEmulateOneCPU"] == "true")
329 if (config
["__MonoTimerPeriod"] != null) {
331 int parsed
= (int)UInt32
.Parse (config
["__MonoTimerPeriod"]);
332 CacheExpires
.EXPIRATIONS_INTERVAL
= new TimeSpan (0, 0, parsed
);
338 _useMemoryCacheManager
= ConfigUtil
.GetBooleanValue(config
, ConfigUtil
.UseMemoryCacheManager
, true);
340 _storeRefs
= new GCHandleRef
<MemoryCacheStore
>[_storeCount
];
342 InitDisposableMembers(config
);
345 private object AddOrGetExistingInternal(string key
, object value, CacheItemPolicy policy
) {
347 throw new ArgumentNullException("key");
349 DateTimeOffset absExp
= ObjectCache
.InfiniteAbsoluteExpiration
;
350 TimeSpan slidingExp
= ObjectCache
.NoSlidingExpiration
;
351 CacheItemPriority priority
= CacheItemPriority
.Default
;
352 Collection
<ChangeMonitor
> changeMonitors
= null;
353 CacheEntryRemovedCallback removedCallback
= null;
354 if (policy
!= null) {
355 ValidatePolicy(policy
);
356 if (policy
.UpdateCallback
!= null) {
357 throw new ArgumentException(R
.Update_callback_must_be_null
, "policy");
359 absExp
= policy
.AbsoluteExpiration
;
360 slidingExp
= policy
.SlidingExpiration
;
361 priority
= policy
.Priority
;
362 changeMonitors
= policy
.ChangeMonitors
;
363 removedCallback
= policy
.RemovedCallback
;
366 if (changeMonitors
!= null) {
367 foreach (ChangeMonitor monitor
in changeMonitors
) {
368 if (monitor
!= null) {
375 MemoryCacheKey cacheKey
= new MemoryCacheKey(key
);
376 MemoryCacheStore store
= GetStore(cacheKey
);
377 MemoryCacheEntry entry
= store
.AddOrGetExisting(cacheKey
, new MemoryCacheEntry(key
, value, absExp
, slidingExp
, priority
, changeMonitors
, removedCallback
, this));
378 return (entry
!= null) ? entry
.Value
: null;
381 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
382 public override CacheEntryChangeMonitor
CreateCacheEntryChangeMonitor(IEnumerable
<String
> keys
, String regionName
= null) {
383 if (regionName
!= null) {
384 throw new NotSupportedException(R
.RegionName_not_supported
);
387 throw new ArgumentNullException("keys");
389 List
<String
> keysClone
= new List
<String
>(keys
);
390 if (keysClone
.Count
== 0) {
391 throw new ArgumentException(RH
.Format(R
.Empty_collection
, "keys"));
394 foreach (string key
in keysClone
) {
396 throw new ArgumentException(RH
.Format(R
.Collection_contains_null_element
, "keys"));
400 return new MemoryCacheEntryChangeMonitor(keysClone
.AsReadOnly(), regionName
, this);
403 public void Dispose() {
404 if (Interlocked
.Exchange(ref _disposed
, 1) == 0) {
405 // unhook domain events
406 DisposeSafeCritical();
407 // stats must be disposed prior to disposing the stores.
408 if (_stats
!= null) {
411 if (_storeRefs
!= null) {
412 foreach (var storeRef
in _storeRefs
) {
413 if (storeRef
!= null) {
418 if (_perfCounters
!= null) {
419 _perfCounters
.Dispose();
421 GC
.SuppressFinalize(this);
425 [SecuritySafeCritical
]
426 [PermissionSet(SecurityAction
.Assert
, Unrestricted
= true)]
427 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification
= "Grandfathered suppression from original caching code checkin")]
428 private void DisposeSafeCritical() {
429 AppDomain appDomain
= Thread
.GetDomain();
430 if (_onAppDomainUnload
!= null) {
431 appDomain
.DomainUnload
-= _onAppDomainUnload
;
433 if (_onUnhandledException
!= null) {
434 appDomain
.UnhandledException
-= _onUnhandledException
;
438 private object GetInternal(string key
, string regionName
) {
439 if (regionName
!= null) {
440 throw new NotSupportedException(R
.RegionName_not_supported
);
443 throw new ArgumentNullException("key");
445 MemoryCacheEntry entry
= GetEntry(key
);
446 return (entry
!= null) ? entry
.Value
: null;
449 internal MemoryCacheEntry
GetEntry(String key
) {
453 MemoryCacheKey cacheKey
= new MemoryCacheKey(key
);
454 MemoryCacheStore store
= GetStore(cacheKey
);
455 return store
.Get(cacheKey
);
458 IEnumerator IEnumerable
.GetEnumerator() {
459 Hashtable h
= new Hashtable();
461 foreach (var storeRef
in _storeRefs
) {
462 storeRef
.Target
.CopyTo(h
);
465 return h
.GetEnumerator();
468 protected override IEnumerator
<KeyValuePair
<string, object>> GetEnumerator() {
469 Dictionary
<string, object> h
= new Dictionary
<string, object>();
471 foreach (var storeRef
in _storeRefs
) {
472 storeRef
.Target
.CopyTo(h
);
475 return h
.GetEnumerator();
478 internal MemoryCacheEntry
RemoveEntry(string key
, MemoryCacheEntry entry
, CacheEntryRemovedReason reason
) {
479 MemoryCacheKey cacheKey
= new MemoryCacheKey(key
);
480 MemoryCacheStore store
= GetStore(cacheKey
);
481 return store
.Remove(cacheKey
, entry
, reason
);
484 public long Trim(int percent
) {
489 if (_disposed
== 0) {
490 foreach (var storeRef
in _storeRefs
) {
491 trimmed
+= storeRef
.Target
.TrimInternal(percent
);
497 //Default indexer property
498 public override object this[string key
] {
500 return GetInternal(key
, null);
503 Set(key
, value, ObjectCache
.InfiniteAbsoluteExpiration
);
507 //Existence check for a single item
508 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
509 public override bool Contains(string key
, string regionName
= null) {
510 return (GetInternal(key
, regionName
) != null);
513 // Dev10 907758: Breaking bug in System.RuntimeCaching.MemoryCache.AddOrGetExisting (CacheItem, CacheItemPolicy)
514 public override bool Add(CacheItem item
, CacheItemPolicy policy
) {
515 CacheItem existingEntry
= AddOrGetExisting(item
, policy
);
516 return (existingEntry
== null || existingEntry
.Value
== null);
519 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
520 public override object AddOrGetExisting(string key
, object value, DateTimeOffset absoluteExpiration
, string regionName
= null) {
521 if (regionName
!= null) {
522 throw new NotSupportedException(R
.RegionName_not_supported
);
524 CacheItemPolicy policy
= new CacheItemPolicy();
525 policy
.AbsoluteExpiration
= absoluteExpiration
;
526 return AddOrGetExistingInternal(key
, value, policy
);
529 public override CacheItem
AddOrGetExisting(CacheItem item
, CacheItemPolicy policy
) {
531 throw new ArgumentNullException("item");
533 return new CacheItem(item
.Key
, AddOrGetExistingInternal(item
.Key
, item
.Value
, policy
));
536 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
537 public override object AddOrGetExisting(string key
, object value, CacheItemPolicy policy
, string regionName
= null) {
538 if (regionName
!= null) {
539 throw new NotSupportedException(R
.RegionName_not_supported
);
541 return AddOrGetExistingInternal(key
, value, policy
);
544 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
545 public override object Get(string key
, string regionName
= null) {
546 return GetInternal(key
, regionName
);
549 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
550 public override CacheItem
GetCacheItem(string key
, string regionName
= null) {
551 object value = GetInternal(key
, regionName
);
552 return (value != null) ? new CacheItem(key
, value) : null;
555 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
556 public override void Set(string key
, object value, DateTimeOffset absoluteExpiration
, string regionName
= null) {
557 if (regionName
!= null) {
558 throw new NotSupportedException(R
.RegionName_not_supported
);
560 CacheItemPolicy policy
= new CacheItemPolicy();
561 policy
.AbsoluteExpiration
= absoluteExpiration
;
562 Set(key
, value, policy
);
565 public override void Set(CacheItem item
, CacheItemPolicy policy
) {
567 throw new ArgumentNullException("item");
569 Set(item
.Key
, item
.Value
, policy
);
572 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
573 public override void Set(string key
, object value, CacheItemPolicy policy
, string regionName
= null) {
574 if (regionName
!= null) {
575 throw new NotSupportedException(R
.RegionName_not_supported
);
578 throw new ArgumentNullException("key");
580 DateTimeOffset absExp
= ObjectCache
.InfiniteAbsoluteExpiration
;
581 TimeSpan slidingExp
= ObjectCache
.NoSlidingExpiration
;
582 CacheItemPriority priority
= CacheItemPriority
.Default
;
583 Collection
<ChangeMonitor
> changeMonitors
= null;
584 CacheEntryRemovedCallback removedCallback
= null;
585 if (policy
!= null) {
586 ValidatePolicy(policy
);
587 if (policy
.UpdateCallback
!= null) {
588 Set(key
, value, policy
.ChangeMonitors
, policy
.AbsoluteExpiration
, policy
.SlidingExpiration
, policy
.UpdateCallback
);
591 absExp
= policy
.AbsoluteExpiration
;
592 slidingExp
= policy
.SlidingExpiration
;
593 priority
= policy
.Priority
;
594 changeMonitors
= policy
.ChangeMonitors
;
595 removedCallback
= policy
.RemovedCallback
;
598 if (changeMonitors
!= null) {
599 foreach (ChangeMonitor monitor
in changeMonitors
) {
600 if (monitor
!= null) {
607 MemoryCacheKey cacheKey
= new MemoryCacheKey(key
);
608 MemoryCacheStore store
= GetStore(cacheKey
);
609 store
.Set(cacheKey
, new MemoryCacheEntry(key
, value, absExp
, slidingExp
, priority
, changeMonitors
, removedCallback
, this));
612 // DevDiv Bugs 162763:
613 // Add a an event that fires *before* an item is evicted from the ASP.NET Cache
614 internal void Set(string key
,
616 Collection
<ChangeMonitor
> changeMonitors
,
617 DateTimeOffset absoluteExpiration
,
618 TimeSpan slidingExpiration
,
619 CacheEntryUpdateCallback onUpdateCallback
) {
621 throw new ArgumentNullException("key");
623 if (changeMonitors
== null
624 && absoluteExpiration
== ObjectCache
.InfiniteAbsoluteExpiration
625 && slidingExpiration
== ObjectCache
.NoSlidingExpiration
) {
626 throw new ArgumentException(R
.Invalid_argument_combination
);
628 if (onUpdateCallback
== null) {
629 throw new ArgumentNullException("onUpdateCallback");
632 if (changeMonitors
!= null) {
633 foreach (ChangeMonitor monitor
in changeMonitors
) {
634 if (monitor
!= null) {
641 // Insert updatable cache entry
642 MemoryCacheKey cacheKey
= new MemoryCacheKey(key
);
643 MemoryCacheStore store
= GetStore(cacheKey
);
644 MemoryCacheEntry cacheEntry
= new MemoryCacheEntry(key
,
646 ObjectCache
.InfiniteAbsoluteExpiration
,
647 ObjectCache
.NoSlidingExpiration
,
648 CacheItemPriority
.NotRemovable
,
652 store
.Set(cacheKey
, cacheEntry
);
654 // Ensure the sentinel depends on its updatable entry
655 string[] cacheKeys
= { key }
;
656 ChangeMonitor expensiveObjectDep
= CreateCacheEntryChangeMonitor(cacheKeys
);
657 if (changeMonitors
== null) {
658 changeMonitors
= new Collection
<ChangeMonitor
>();
660 changeMonitors
.Add(expensiveObjectDep
);
662 // Insert sentinel entry for the updatable cache entry
663 MemoryCacheKey sentinelCacheKey
= new MemoryCacheKey("OnUpdateSentinel" + key
);
664 MemoryCacheStore sentinelStore
= GetStore(sentinelCacheKey
);
665 MemoryCacheEntry sentinelCacheEntry
= new MemoryCacheEntry(sentinelCacheKey
.Key
,
666 new SentinelEntry(key
, expensiveObjectDep
, onUpdateCallback
),
669 CacheItemPriority
.NotRemovable
,
671 s_sentinelRemovedCallback
,
673 sentinelStore
.Set(sentinelCacheKey
, sentinelCacheEntry
);
674 cacheEntry
.ConfigureUpdateSentinel(sentinelStore
, sentinelCacheEntry
);
677 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
678 public override object Remove(string key
, string regionName
= null) {
679 return Remove(key
, CacheEntryRemovedReason
.Removed
, regionName
);
682 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
= "This is assembly is a special case approved by the NetFx API review board")]
683 public object Remove(string key
, CacheEntryRemovedReason reason
, string regionName
= null) {
684 if (regionName
!= null) {
685 throw new NotSupportedException(R
.RegionName_not_supported
);
688 throw new ArgumentNullException("key");
693 MemoryCacheEntry entry
= RemoveEntry(key
, null, reason
);
694 return (entry
!= null) ? entry
.Value
: null;
697 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
698 public override long GetCount(string regionName
= null) {
699 if (regionName
!= null) {
700 throw new NotSupportedException(R
.RegionName_not_supported
);
704 foreach (var storeRef
in _storeRefs
) {
705 count
+= storeRef
.Target
.Count
;
711 public long GetLastSize(string regionName
= null) {
712 if (regionName
!= null) {
713 throw new NotSupportedException(R
.RegionName_not_supported
);
716 return _stats
.GetLastSize();
719 [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification
="This is assembly is a special case approved by the NetFx API review board")]
720 public override IDictionary
<string, object> GetValues(IEnumerable
<String
> keys
, string regionName
= null) {
721 if (regionName
!= null) {
722 throw new NotSupportedException(R
.RegionName_not_supported
);
725 throw new ArgumentNullException("keys");
727 Dictionary
<string, object> values
= null;
729 foreach (string key
in keys
) {
731 throw new ArgumentException(RH
.Format(R
.Collection_contains_null_element
, "keys"));
733 object value = GetInternal(key
, null);
735 if (values
== null) {
736 values
= new Dictionary
<string, object>();
745 // used when redirecting ASP.NET cache into the MemoryCache. This avoids infinite recursion
746 // due to the fact that the (ASP.NET) config system uses the cache, and the cache uses the
748 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification
= "Grandfathered suppression from original caching code checkin")]
749 internal void UpdateConfig(NameValueCollection config
) {
750 if (config
== null) {
751 throw new ArgumentNullException("config");
754 _stats
.UpdateConfig(config
);