Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Runtime.Caching / System / Caching / MemoryCache.cs
blob52c3fd7f7347497239426f50f113a1965a79ef4a
1 // <copyright file="MemoryCache.cs" company="Microsoft">
2 // Copyright (c) 2009 Microsoft Corporation. All rights reserved.
3 // </copyright>
4 using System;
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;
34 private string _name;
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 {
45 private string _key;
46 private ChangeMonitor _expensiveObjectDependency;
47 private CacheEntryUpdateCallback _updateCallback;
49 internal SentinelEntry(string key, ChangeMonitor expensiveObjectDependency, CacheEntryUpdateCallback callback) {
50 _key = key;
51 _expensiveObjectDependency = expensiveObjectDependency;
52 _updateCallback = callback;
55 internal string Key {
56 get { return _key; }
59 internal ChangeMonitor ExpensiveObjectDependency {
60 get { return _expensiveObjectDependency; }
63 internal CacheEntryUpdateCallback CacheEntryUpdateCallback {
64 get { return _updateCallback; }
67 private static bool IsPolicyValid(CacheItemPolicy policy) {
68 if (policy == null) {
69 return false;
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) {
77 hasChanged = true;
78 break;
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) {
85 return true;
87 // if the monitors have changed we need to dispose them
88 if (hasChanged) {
89 foreach (ChangeMonitor monitor in changeMonitors) {
90 if (monitor != null) {
91 monitor.Dispose();
95 return false;
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;
102 switch (reason) {
103 case CacheEntryRemovedReason.Expired:
104 break;
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.
110 return;
112 break;
113 case CacheEntryRemovedReason.Evicted:
114 Dbg.Fail("Reason should never be CacheEntryRemovedReason.Evicted since the entry was inserted as NotRemovable.");
115 return;
116 default:
117 // do nothing if reason is Removed or CacheSpecificEviction
118 return;
121 // invoke update callback
122 try {
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);
133 else {
134 cache.Remove(entry.Key);
137 catch {
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;
150 if (hashCode < 0) {
151 hashCode = (hashCode == Int32.MinValue) ? 0 : -hashCode;
153 int idx = hashCode % _storeCount;
154 return _storeRefs[idx].Target;
157 internal object[] AllSRefTargets {
158 get {
159 var allStores = new MemoryCacheStore[_storeCount];
160 for (int i = 0; i < _storeCount; i++) {
161 allStores[i] = _storeRefs[i].Target;
163 return allStores;
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) {
171 bool dispose = true;
172 try {
173 try {
174 _perfCounters = new PerfCounters(_name);
176 catch {
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;
190 dispose = false;
192 finally {
193 if (dispose) {
194 Dispose();
199 private void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) {
200 Dispose();
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) {
207 Dispose();
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));
228 // public
230 // Amount of memory that can be used before
231 // the cache begins to forcibly remove items.
232 public long CacheMemoryLimit {
233 get {
234 return _stats.CacheMemoryLimit;
238 public static MemoryCache Default {
239 get {
240 if (s_defaultCache == null) {
241 lock (s_initLock) {
242 if (s_defaultCache == null) {
243 s_defaultCache = new MemoryCache();
247 return s_defaultCache;
251 public override DefaultCacheCapabilities DefaultCacheCapabilities {
252 get {
253 return CAPABILITIES;
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 {
269 get {
270 return _stats.PhysicalMemoryLimit;
274 // The maximum interval of time afterwhich the cache
275 // will update its memory statistics.
276 public TimeSpan PollingInterval {
277 get {
278 return _stats.PollingInterval;
282 // Only used for Default MemoryCache
283 private MemoryCache() {
284 _name = "Default";
285 Init(null);
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) {
290 if (name == 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");
299 _name = name;
300 Init(config);
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) {
308 if (name == null) {
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");
317 _name = name;
318 _configLess = ignoreConfigSection;
319 Init(config);
323 private void Init(NameValueCollection config) {
324 _storeCount = Environment.ProcessorCount;
325 if (config != null) {
326 #if MONO
327 if (config ["__MonoEmulateOneCPU"] == "true")
328 _storeCount = 1;
329 if (config ["__MonoTimerPeriod"] != null) {
330 try {
331 int parsed = (int)UInt32.Parse (config ["__MonoTimerPeriod"]);
332 CacheExpires.EXPIRATIONS_INTERVAL = new TimeSpan (0, 0, parsed);
333 } catch {
337 #endif
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) {
346 if (key == null) {
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;
365 if (IsDisposed) {
366 if (changeMonitors != null) {
367 foreach (ChangeMonitor monitor in changeMonitors) {
368 if (monitor != null) {
369 monitor.Dispose();
373 return 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);
386 if (keys == null) {
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) {
395 if (key == null) {
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) {
409 _stats.Dispose();
411 if (_storeRefs != null) {
412 foreach (var storeRef in _storeRefs) {
413 if (storeRef != null) {
414 storeRef.Dispose();
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);
442 if (key == null) {
443 throw new ArgumentNullException("key");
445 MemoryCacheEntry entry = GetEntry(key);
446 return (entry != null) ? entry.Value : null;
449 internal MemoryCacheEntry GetEntry(String key) {
450 if (IsDisposed) {
451 return null;
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();
460 if (!IsDisposed) {
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>();
470 if (!IsDisposed) {
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) {
485 if (percent > 100) {
486 percent = 100;
488 long trimmed = 0;
489 if (_disposed == 0) {
490 foreach (var storeRef in _storeRefs) {
491 trimmed += storeRef.Target.TrimInternal(percent);
494 return trimmed;
497 //Default indexer property
498 public override object this[string key] {
499 get {
500 return GetInternal(key, null);
502 set {
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) {
530 if (item == null) {
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) {
566 if (item == null) {
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);
577 if (key == null) {
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);
589 return;
591 absExp = policy.AbsoluteExpiration;
592 slidingExp = policy.SlidingExpiration;
593 priority = policy.Priority;
594 changeMonitors = policy.ChangeMonitors;
595 removedCallback = policy.RemovedCallback;
597 if (IsDisposed) {
598 if (changeMonitors != null) {
599 foreach (ChangeMonitor monitor in changeMonitors) {
600 if (monitor != null) {
601 monitor.Dispose();
605 return;
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,
615 object value,
616 Collection<ChangeMonitor> changeMonitors,
617 DateTimeOffset absoluteExpiration,
618 TimeSpan slidingExpiration,
619 CacheEntryUpdateCallback onUpdateCallback) {
620 if (key == null) {
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");
631 if (IsDisposed) {
632 if (changeMonitors != null) {
633 foreach (ChangeMonitor monitor in changeMonitors) {
634 if (monitor != null) {
635 monitor.Dispose();
639 return;
641 // Insert updatable cache entry
642 MemoryCacheKey cacheKey = new MemoryCacheKey(key);
643 MemoryCacheStore store = GetStore(cacheKey);
644 MemoryCacheEntry cacheEntry = new MemoryCacheEntry(key,
645 value,
646 ObjectCache.InfiniteAbsoluteExpiration,
647 ObjectCache.NoSlidingExpiration,
648 CacheItemPriority.NotRemovable,
649 null,
650 null,
651 this);
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),
667 absoluteExpiration,
668 slidingExpiration,
669 CacheItemPriority.NotRemovable,
670 changeMonitors,
671 s_sentinelRemovedCallback,
672 this);
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);
687 if (key == null) {
688 throw new ArgumentNullException("key");
690 if (IsDisposed) {
691 return null;
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);
702 long count = 0;
703 if (!IsDisposed) {
704 foreach (var storeRef in _storeRefs) {
705 count += storeRef.Target.Count;
708 return 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);
724 if (keys == null) {
725 throw new ArgumentNullException("keys");
727 Dictionary<string, object> values = null;
728 if (!IsDisposed) {
729 foreach (string key in keys) {
730 if (key == null) {
731 throw new ArgumentException(RH.Format(R.Collection_contains_null_element, "keys"));
733 object value = GetInternal(key, null);
734 if (value != null) {
735 if (values == null) {
736 values = new Dictionary<string, object>();
738 values[key] = value;
742 return values;
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
747 // config system.
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");
753 if (!IsDisposed) {
754 _stats.UpdateConfig(config);