2016-08-03 10:59:49 +00:00
// <copyright file="MemoryCache.cs" company="Microsoft">
// Copyright (c) 2009 Microsoft Corporation. All rights reserved.
// </copyright>
using System ;
using System.Runtime.Caching.Configuration ;
using System.Runtime.Caching.Resources ;
using System.Collections ;
using System.Collections.Generic ;
using System.Collections.Specialized ;
using System.Collections.ObjectModel ;
using System.Configuration ;
using System.Diagnostics.CodeAnalysis ;
using System.Security ;
using System.Security.Permissions ;
using System.Threading ;
namespace System.Runtime.Caching {
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification= "The class represents a type of cache")]
public class MemoryCache : ObjectCache , IEnumerable , IDisposable {
private const DefaultCacheCapabilities CAPABILITIES = DefaultCacheCapabilities . InMemoryProvider
| DefaultCacheCapabilities . CacheEntryChangeMonitors
| DefaultCacheCapabilities . AbsoluteExpirations
| DefaultCacheCapabilities . SlidingExpirations
| DefaultCacheCapabilities . CacheEntryUpdateCallback
| DefaultCacheCapabilities . CacheEntryRemovedCallback ;
private static readonly TimeSpan OneYear = new TimeSpan ( 365 , 0 , 0 , 0 ) ;
private static object s_initLock = new object ( ) ;
private static MemoryCache s_defaultCache ;
private static CacheEntryRemovedCallback s_sentinelRemovedCallback = new CacheEntryRemovedCallback ( SentinelEntry . OnCacheEntryRemovedCallback ) ;
2016-11-10 13:04:39 +00:00
private GCHandleRef < MemoryCacheStore > [ ] _storeRefs ;
2016-08-03 10:59:49 +00:00
private int _storeCount ;
private int _disposed ;
private MemoryCacheStatistics _stats ;
private string _name ;
private PerfCounters _perfCounters ;
private bool _configLess ;
2017-08-21 15:34:15 +00:00
private bool _useMemoryCacheManager = true ;
2016-08-03 10:59:49 +00:00
EventHandler _onAppDomainUnload ;
UnhandledExceptionEventHandler _onUnhandledException ;
private bool IsDisposed { get { return ( _disposed = = 1 ) ; } }
internal bool ConfigLess { get { return _configLess ; } }
private class SentinelEntry {
private string _key ;
private ChangeMonitor _expensiveObjectDependency ;
private CacheEntryUpdateCallback _updateCallback ;
internal SentinelEntry ( string key , ChangeMonitor expensiveObjectDependency , CacheEntryUpdateCallback callback ) {
_key = key ;
_expensiveObjectDependency = expensiveObjectDependency ;
_updateCallback = callback ;
}
internal string Key {
get { return _key ; }
}
internal ChangeMonitor ExpensiveObjectDependency {
get { return _expensiveObjectDependency ; }
}
internal CacheEntryUpdateCallback CacheEntryUpdateCallback {
get { return _updateCallback ; }
}
private static bool IsPolicyValid ( CacheItemPolicy policy ) {
if ( policy = = null ) {
return false ;
}
// see if any change monitors have changed
bool hasChanged = false ;
Collection < ChangeMonitor > changeMonitors = policy . ChangeMonitors ;
if ( changeMonitors ! = null ) {
foreach ( ChangeMonitor monitor in changeMonitors ) {
if ( monitor ! = null & & monitor . HasChanged ) {
hasChanged = true ;
break ;
}
}
}
// if the monitors haven't changed yet and we have an update callback
// then the policy is valid
if ( ! hasChanged & & policy . UpdateCallback ! = null ) {
return true ;
}
// if the monitors have changed we need to dispose them
if ( hasChanged ) {
foreach ( ChangeMonitor monitor in changeMonitors ) {
if ( monitor ! = null ) {
monitor . Dispose ( ) ;
}
}
}
return false ;
}
internal static void OnCacheEntryRemovedCallback ( CacheEntryRemovedArguments arguments ) {
MemoryCache cache = arguments . Source as MemoryCache ;
SentinelEntry entry = arguments . CacheItem . Value as SentinelEntry ;
CacheEntryRemovedReason reason = arguments . RemovedReason ;
switch ( reason ) {
case CacheEntryRemovedReason . Expired :
break ;
case CacheEntryRemovedReason . ChangeMonitorChanged :
if ( entry . ExpensiveObjectDependency . HasChanged ) {
// If the expensiveObject has been removed explicitly by Cache.Remove,
// return from the SentinelEntry removed callback
// thus effectively removing the SentinelEntry from the cache.
return ;
}
break ;
case CacheEntryRemovedReason . Evicted :
Dbg . Fail ( "Reason should never be CacheEntryRemovedReason.Evicted since the entry was inserted as NotRemovable." ) ;
return ;
default :
// do nothing if reason is Removed or CacheSpecificEviction
return ;
}
// invoke update callback
try {
CacheEntryUpdateArguments args = new CacheEntryUpdateArguments ( cache , reason , entry . Key , null ) ;
entry . CacheEntryUpdateCallback ( args ) ;
Object expensiveObject = ( args . UpdatedCacheItem ! = null ) ? args . UpdatedCacheItem . Value : null ;
CacheItemPolicy policy = args . UpdatedCacheItemPolicy ;
// Dev10 861163 - Only update the "expensive" object if the user returns a new object,
// a policy with update callback, and the change monitors haven't changed. (Inserting
// with change monitors that have already changed will cause recursion.)
if ( expensiveObject ! = null & & IsPolicyValid ( policy ) ) {
cache . Set ( entry . Key , expensiveObject , policy ) ;
}
else {
cache . Remove ( entry . Key ) ;
}
}
catch {
cache . Remove ( entry . Key ) ;
// Review: What should we do with this exception?
}
}
}
// private and internal
internal MemoryCacheStore GetStore ( MemoryCacheKey cacheKey ) {
// Dev10 865907: Math.Abs throws OverflowException for Int32.MinValue
int hashCode = cacheKey . Hash ;
if ( hashCode < 0 ) {
hashCode = ( hashCode = = Int32 . MinValue ) ? 0 : - hashCode ;
}
2016-11-10 13:04:39 +00:00
int idx = hashCode % _storeCount ;
return _storeRefs [ idx ] . Target ;
}
internal object [ ] AllSRefTargets {
get {
var allStores = new MemoryCacheStore [ _storeCount ] ;
for ( int i = 0 ; i < _storeCount ; i + + ) {
allStores [ i ] = _storeRefs [ i ] . Target ;
}
return allStores ;
}
2016-08-03 10:59:49 +00:00
}
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "Grandfathered suppression from original caching code checkin")]
private void InitDisposableMembers ( NameValueCollection config ) {
bool dispose = true ;
try {
try {
_perfCounters = new PerfCounters ( _name ) ;
}
catch {
// ignore exceptions from perf counters
}
2016-11-10 13:04:39 +00:00
for ( int i = 0 ; i < _storeCount ; i + + ) {
_storeRefs [ i ] = new GCHandleRef < MemoryCacheStore > ( new MemoryCacheStore ( this , _perfCounters ) ) ;
2016-08-03 10:59:49 +00:00
}
_stats = new MemoryCacheStatistics ( this , config ) ;
AppDomain appDomain = Thread . GetDomain ( ) ;
EventHandler onAppDomainUnload = new EventHandler ( OnAppDomainUnload ) ;
appDomain . DomainUnload + = onAppDomainUnload ;
_onAppDomainUnload = onAppDomainUnload ;
UnhandledExceptionEventHandler onUnhandledException = new UnhandledExceptionEventHandler ( OnUnhandledException ) ;
appDomain . UnhandledException + = onUnhandledException ;
_onUnhandledException = onUnhandledException ;
dispose = false ;
}
finally {
if ( dispose ) {
Dispose ( ) ;
}
}
}
private void OnAppDomainUnload ( Object unusedObject , EventArgs unusedEventArgs ) {
Dispose ( ) ;
}
private void OnUnhandledException ( Object sender , UnhandledExceptionEventArgs eventArgs ) {
// if the CLR is terminating, dispose the cache.
// This will dispose the perf counters (see Dev10 680819).
if ( eventArgs . IsTerminating ) {
Dispose ( ) ;
}
}
private void ValidatePolicy ( CacheItemPolicy policy ) {
if ( policy . AbsoluteExpiration ! = ObjectCache . InfiniteAbsoluteExpiration
& & policy . SlidingExpiration ! = ObjectCache . NoSlidingExpiration ) {
throw new ArgumentException ( R . Invalid_expiration_combination , "policy" ) ;
}
if ( policy . SlidingExpiration < ObjectCache . NoSlidingExpiration | | OneYear < policy . SlidingExpiration ) {
throw new ArgumentOutOfRangeException ( "policy" , RH . Format ( R . Argument_out_of_range , "SlidingExpiration" , ObjectCache . NoSlidingExpiration , OneYear ) ) ;
}
if ( policy . RemovedCallback ! = null
& & policy . UpdateCallback ! = null ) {
throw new ArgumentException ( R . Invalid_callback_combination , "policy" ) ;
}
if ( policy . Priority ! = CacheItemPriority . Default & & policy . Priority ! = CacheItemPriority . NotRemovable ) {
throw new ArgumentOutOfRangeException ( "policy" , RH . Format ( R . Argument_out_of_range , "Priority" , CacheItemPriority . Default , CacheItemPriority . NotRemovable ) ) ;
}
}
// public
// Amount of memory that can be used before
// the cache begins to forcibly remove items.
public long CacheMemoryLimit {
get {
return _stats . CacheMemoryLimit ;
}
}
public static MemoryCache Default {
get {
if ( s_defaultCache = = null ) {
lock ( s_initLock ) {
if ( s_defaultCache = = null ) {
s_defaultCache = new MemoryCache ( ) ;
}
}
}
return s_defaultCache ;
}
}
public override DefaultCacheCapabilities DefaultCacheCapabilities {
get {
return CAPABILITIES ;
}
}
public override string Name
{
get { return _name ; }
}
2017-08-21 15:34:15 +00:00
internal bool UseMemoryCacheManager {
get { return _useMemoryCacheManager ; }
}
2016-08-03 10:59:49 +00:00
// Percentage of physical memory that can be used before
// the cache begins to forcibly remove items.
public long PhysicalMemoryLimit {
get {
return _stats . PhysicalMemoryLimit ;
}
}
// The maximum interval of time afterwhich the cache
// will update its memory statistics.
public TimeSpan PollingInterval {
get {
return _stats . PollingInterval ;
}
}
// Only used for Default MemoryCache
private MemoryCache ( ) {
_name = "Default" ;
Init ( null ) ;
}
2017-08-21 15:34:15 +00:00
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is assembly is a special case approved by the NetFx API review board")]
2016-08-03 10:59:49 +00:00
public MemoryCache ( string name , NameValueCollection config = null ) {
if ( name = = null ) {
throw new ArgumentNullException ( "name" ) ;
}
if ( name = = String . Empty ) {
throw new ArgumentException ( R . Empty_string_invalid , "name" ) ;
}
if ( String . Equals ( name , "default" , StringComparison . OrdinalIgnoreCase ) ) {
throw new ArgumentException ( R . Default_is_reserved , "name" ) ;
}
_name = name ;
Init ( config ) ;
}
2017-08-21 15:34:15 +00:00
// ignoreConfigSection is used when redirecting ASP.NET cache into the MemoryCache. This avoids infinite recursion
2016-08-03 10:59:49 +00:00
// due to the fact that the (ASP.NET) config system uses the cache, and the cache uses the
// config system. This method could be made public, perhaps with CAS to prevent partial trust callers.
2017-08-21 15:34:15 +00:00
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is assembly is a special case approved by the NetFx API review board")]
public MemoryCache ( string name , NameValueCollection config , bool ignoreConfigSection ) {
2016-08-03 10:59:49 +00:00
if ( name = = null ) {
throw new ArgumentNullException ( "name" ) ;
}
if ( name = = String . Empty ) {
throw new ArgumentException ( R . Empty_string_invalid , "name" ) ;
}
if ( String . Equals ( name , "default" , StringComparison . OrdinalIgnoreCase ) ) {
throw new ArgumentException ( R . Default_is_reserved , "name" ) ;
}
_name = name ;
2017-08-21 15:34:15 +00:00
_configLess = ignoreConfigSection ;
2016-08-03 10:59:49 +00:00
Init ( config ) ;
}
2017-08-21 15:34:15 +00:00
2016-08-03 10:59:49 +00:00
private void Init ( NameValueCollection config ) {
_storeCount = Environment . ProcessorCount ;
if ( config ! = null ) {
2017-08-21 15:34:15 +00:00
#if MONO
2016-08-03 10:59:49 +00:00
if ( config [ "__MonoEmulateOneCPU" ] = = "true" )
_storeCount = 1 ;
if ( config [ "__MonoTimerPeriod" ] ! = null ) {
try {
int parsed = ( int ) UInt32 . Parse ( config [ "__MonoTimerPeriod" ] ) ;
CacheExpires . EXPIRATIONS_INTERVAL = new TimeSpan ( 0 , 0 , parsed ) ;
} catch {
//
}
}
2017-08-21 15:34:15 +00:00
#endif
_useMemoryCacheManager = ConfigUtil . GetBooleanValue ( config , ConfigUtil . UseMemoryCacheManager , true ) ;
2016-08-03 10:59:49 +00:00
}
2016-11-10 13:04:39 +00:00
_storeRefs = new GCHandleRef < MemoryCacheStore > [ _storeCount ] ;
2017-08-21 15:34:15 +00:00
2016-08-03 10:59:49 +00:00
InitDisposableMembers ( config ) ;
}
private object AddOrGetExistingInternal ( string key , object value , CacheItemPolicy policy ) {
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
DateTimeOffset absExp = ObjectCache . InfiniteAbsoluteExpiration ;
TimeSpan slidingExp = ObjectCache . NoSlidingExpiration ;
CacheItemPriority priority = CacheItemPriority . Default ;
Collection < ChangeMonitor > changeMonitors = null ;
CacheEntryRemovedCallback removedCallback = null ;
if ( policy ! = null ) {
ValidatePolicy ( policy ) ;
if ( policy . UpdateCallback ! = null ) {
throw new ArgumentException ( R . Update_callback_must_be_null , "policy" ) ;
}
absExp = policy . AbsoluteExpiration ;
slidingExp = policy . SlidingExpiration ;
priority = policy . Priority ;
changeMonitors = policy . ChangeMonitors ;
removedCallback = policy . RemovedCallback ;
}
if ( IsDisposed ) {
if ( changeMonitors ! = null ) {
foreach ( ChangeMonitor monitor in changeMonitors ) {
if ( monitor ! = null ) {
monitor . Dispose ( ) ;
}
}
}
return null ;
}
MemoryCacheKey cacheKey = new MemoryCacheKey ( key ) ;
MemoryCacheStore store = GetStore ( cacheKey ) ;
MemoryCacheEntry entry = store . AddOrGetExisting ( cacheKey , new MemoryCacheEntry ( key , value , absExp , slidingExp , priority , changeMonitors , removedCallback , this ) ) ;
return ( entry ! = null ) ? entry . Value : null ;
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor ( IEnumerable < String > keys , String regionName = null ) {
if ( regionName ! = null ) {
throw new NotSupportedException ( R . RegionName_not_supported ) ;
}
if ( keys = = null ) {
throw new ArgumentNullException ( "keys" ) ;
}
List < String > keysClone = new List < String > ( keys ) ;
if ( keysClone . Count = = 0 ) {
throw new ArgumentException ( RH . Format ( R . Empty_collection , "keys" ) ) ;
}
foreach ( string key in keysClone ) {
if ( key = = null ) {
throw new ArgumentException ( RH . Format ( R . Collection_contains_null_element , "keys" ) ) ;
}
}
return new MemoryCacheEntryChangeMonitor ( keysClone . AsReadOnly ( ) , regionName , this ) ;
}
public void Dispose ( ) {
if ( Interlocked . Exchange ( ref _disposed , 1 ) = = 0 ) {
// unhook domain events
DisposeSafeCritical ( ) ;
// stats must be disposed prior to disposing the stores.
if ( _stats ! = null ) {
_stats . Dispose ( ) ;
}
2016-11-10 13:04:39 +00:00
if ( _storeRefs ! = null ) {
foreach ( var storeRef in _storeRefs ) {
if ( storeRef ! = null ) {
storeRef . Dispose ( ) ;
2016-08-03 10:59:49 +00:00
}
}
}
if ( _perfCounters ! = null ) {
_perfCounters . Dispose ( ) ;
}
GC . SuppressFinalize ( this ) ;
}
}
[SecuritySafeCritical]
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "Grandfathered suppression from original caching code checkin")]
private void DisposeSafeCritical ( ) {
AppDomain appDomain = Thread . GetDomain ( ) ;
if ( _onAppDomainUnload ! = null ) {
appDomain . DomainUnload - = _onAppDomainUnload ;
}
if ( _onUnhandledException ! = null ) {
appDomain . UnhandledException - = _onUnhandledException ;
}
}
private object GetInternal ( string key , string regionName ) {
if ( regionName ! = null ) {
throw new NotSupportedException ( R . RegionName_not_supported ) ;
}
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
MemoryCacheEntry entry = GetEntry ( key ) ;
return ( entry ! = null ) ? entry . Value : null ;
}
internal MemoryCacheEntry GetEntry ( String key ) {
if ( IsDisposed ) {
return null ;
}
MemoryCacheKey cacheKey = new MemoryCacheKey ( key ) ;
MemoryCacheStore store = GetStore ( cacheKey ) ;
return store . Get ( cacheKey ) ;
}
IEnumerator IEnumerable . GetEnumerator ( ) {
Hashtable h = new Hashtable ( ) ;
if ( ! IsDisposed ) {
2016-11-10 13:04:39 +00:00
foreach ( var storeRef in _storeRefs ) {
storeRef . Target . CopyTo ( h ) ;
2016-08-03 10:59:49 +00:00
}
}
return h . GetEnumerator ( ) ;
}
protected override IEnumerator < KeyValuePair < string , object > > GetEnumerator ( ) {
Dictionary < string , object > h = new Dictionary < string , object > ( ) ;
if ( ! IsDisposed ) {
2016-11-10 13:04:39 +00:00
foreach ( var storeRef in _storeRefs ) {
storeRef . Target . CopyTo ( h ) ;
2016-08-03 10:59:49 +00:00
}
}
return h . GetEnumerator ( ) ;
}
internal MemoryCacheEntry RemoveEntry ( string key , MemoryCacheEntry entry , CacheEntryRemovedReason reason ) {
MemoryCacheKey cacheKey = new MemoryCacheKey ( key ) ;
MemoryCacheStore store = GetStore ( cacheKey ) ;
return store . Remove ( cacheKey , entry , reason ) ;
}
public long Trim ( int percent ) {
if ( percent > 100 ) {
percent = 100 ;
}
long trimmed = 0 ;
if ( _disposed = = 0 ) {
2016-11-10 13:04:39 +00:00
foreach ( var storeRef in _storeRefs ) {
trimmed + = storeRef . Target . TrimInternal ( percent ) ;
2016-08-03 10:59:49 +00:00
}
}
return trimmed ;
}
//Default indexer property
public override object this [ string key ] {
get {
return GetInternal ( key , null ) ;
}
set {
Set ( key , value , ObjectCache . InfiniteAbsoluteExpiration ) ;
}
}
//Existence check for a single item
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override bool Contains ( string key , string regionName = null ) {
return ( GetInternal ( key , regionName ) ! = null ) ;
}
// Dev10 907758: Breaking bug in System.RuntimeCaching.MemoryCache.AddOrGetExisting (CacheItem, CacheItemPolicy)
public override bool Add ( CacheItem item , CacheItemPolicy policy ) {
CacheItem existingEntry = AddOrGetExisting ( item , policy ) ;
return ( existingEntry = = null | | existingEntry . Value = = null ) ;
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override object AddOrGetExisting ( string key , object value , DateTimeOffset absoluteExpiration , string regionName = null ) {
if ( regionName ! = null ) {
throw new NotSupportedException ( R . RegionName_not_supported ) ;
}
CacheItemPolicy policy = new CacheItemPolicy ( ) ;
policy . AbsoluteExpiration = absoluteExpiration ;
return AddOrGetExistingInternal ( key , value , policy ) ;
}
public override CacheItem AddOrGetExisting ( CacheItem item , CacheItemPolicy policy ) {
if ( item = = null ) {
throw new ArgumentNullException ( "item" ) ;
}
return new CacheItem ( item . Key , AddOrGetExistingInternal ( item . Key , item . Value , policy ) ) ;
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override object AddOrGetExisting ( string key , object value , CacheItemPolicy policy , string regionName = null ) {
if ( regionName ! = null ) {
throw new NotSupportedException ( R . RegionName_not_supported ) ;
}
return AddOrGetExistingInternal ( key , value , policy ) ;
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override object Get ( string key , string regionName = null ) {
return GetInternal ( key , regionName ) ;
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override CacheItem GetCacheItem ( string key , string regionName = null ) {
object value = GetInternal ( key , regionName ) ;
return ( value ! = null ) ? new CacheItem ( key , value ) : null ;
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override void Set ( string key , object value , DateTimeOffset absoluteExpiration , string regionName = null ) {
if ( regionName ! = null ) {
throw new NotSupportedException ( R . RegionName_not_supported ) ;
}
CacheItemPolicy policy = new CacheItemPolicy ( ) ;
policy . AbsoluteExpiration = absoluteExpiration ;
Set ( key , value , policy ) ;
}
public override void Set ( CacheItem item , CacheItemPolicy policy ) {
if ( item = = null ) {
throw new ArgumentNullException ( "item" ) ;
}
Set ( item . Key , item . Value , policy ) ;
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override void Set ( string key , object value , CacheItemPolicy policy , string regionName = null ) {
if ( regionName ! = null ) {
throw new NotSupportedException ( R . RegionName_not_supported ) ;
}
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
DateTimeOffset absExp = ObjectCache . InfiniteAbsoluteExpiration ;
TimeSpan slidingExp = ObjectCache . NoSlidingExpiration ;
CacheItemPriority priority = CacheItemPriority . Default ;
Collection < ChangeMonitor > changeMonitors = null ;
CacheEntryRemovedCallback removedCallback = null ;
if ( policy ! = null ) {
ValidatePolicy ( policy ) ;
if ( policy . UpdateCallback ! = null ) {
Set ( key , value , policy . ChangeMonitors , policy . AbsoluteExpiration , policy . SlidingExpiration , policy . UpdateCallback ) ;
return ;
}
absExp = policy . AbsoluteExpiration ;
slidingExp = policy . SlidingExpiration ;
priority = policy . Priority ;
changeMonitors = policy . ChangeMonitors ;
removedCallback = policy . RemovedCallback ;
}
if ( IsDisposed ) {
if ( changeMonitors ! = null ) {
foreach ( ChangeMonitor monitor in changeMonitors ) {
if ( monitor ! = null ) {
monitor . Dispose ( ) ;
}
}
}
return ;
}
MemoryCacheKey cacheKey = new MemoryCacheKey ( key ) ;
MemoryCacheStore store = GetStore ( cacheKey ) ;
store . Set ( cacheKey , new MemoryCacheEntry ( key , value , absExp , slidingExp , priority , changeMonitors , removedCallback , this ) ) ;
}
// DevDiv Bugs 162763:
// Add a an event that fires *before* an item is evicted from the ASP.NET Cache
internal void Set ( string key ,
object value ,
Collection < ChangeMonitor > changeMonitors ,
DateTimeOffset absoluteExpiration ,
TimeSpan slidingExpiration ,
CacheEntryUpdateCallback onUpdateCallback ) {
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
if ( changeMonitors = = null
& & absoluteExpiration = = ObjectCache . InfiniteAbsoluteExpiration
& & slidingExpiration = = ObjectCache . NoSlidingExpiration ) {
throw new ArgumentException ( R . Invalid_argument_combination ) ;
}
if ( onUpdateCallback = = null ) {
throw new ArgumentNullException ( "onUpdateCallback" ) ;
}
if ( IsDisposed ) {
if ( changeMonitors ! = null ) {
foreach ( ChangeMonitor monitor in changeMonitors ) {
if ( monitor ! = null ) {
monitor . Dispose ( ) ;
}
}
}
return ;
}
// Insert updatable cache entry
MemoryCacheKey cacheKey = new MemoryCacheKey ( key ) ;
MemoryCacheStore store = GetStore ( cacheKey ) ;
MemoryCacheEntry cacheEntry = new MemoryCacheEntry ( key ,
value ,
ObjectCache . InfiniteAbsoluteExpiration ,
ObjectCache . NoSlidingExpiration ,
CacheItemPriority . NotRemovable ,
null ,
null ,
this ) ;
store . Set ( cacheKey , cacheEntry ) ;
// Ensure the sentinel depends on its updatable entry
string [ ] cacheKeys = { key } ;
ChangeMonitor expensiveObjectDep = CreateCacheEntryChangeMonitor ( cacheKeys ) ;
if ( changeMonitors = = null ) {
changeMonitors = new Collection < ChangeMonitor > ( ) ;
}
changeMonitors . Add ( expensiveObjectDep ) ;
// Insert sentinel entry for the updatable cache entry
MemoryCacheKey sentinelCacheKey = new MemoryCacheKey ( "OnUpdateSentinel" + key ) ;
MemoryCacheStore sentinelStore = GetStore ( sentinelCacheKey ) ;
MemoryCacheEntry sentinelCacheEntry = new MemoryCacheEntry ( sentinelCacheKey . Key ,
new SentinelEntry ( key , expensiveObjectDep , onUpdateCallback ) ,
absoluteExpiration ,
slidingExpiration ,
CacheItemPriority . NotRemovable ,
changeMonitors ,
s_sentinelRemovedCallback ,
this ) ;
sentinelStore . Set ( sentinelCacheKey , sentinelCacheEntry ) ;
cacheEntry . ConfigureUpdateSentinel ( sentinelStore , sentinelCacheEntry ) ;
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override object Remove ( string key , string regionName = null ) {
2017-08-21 15:34:15 +00:00
return Remove ( key , CacheEntryRemovedReason . Removed , regionName ) ;
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is assembly is a special case approved by the NetFx API review board")]
public object Remove ( string key , CacheEntryRemovedReason reason , string regionName = null ) {
2016-08-03 10:59:49 +00:00
if ( regionName ! = null ) {
throw new NotSupportedException ( R . RegionName_not_supported ) ;
}
if ( key = = null ) {
throw new ArgumentNullException ( "key" ) ;
}
if ( IsDisposed ) {
return null ;
}
2017-08-21 15:34:15 +00:00
MemoryCacheEntry entry = RemoveEntry ( key , null , reason ) ;
2016-08-03 10:59:49 +00:00
return ( entry ! = null ) ? entry . Value : null ;
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override long GetCount ( string regionName = null ) {
if ( regionName ! = null ) {
throw new NotSupportedException ( R . RegionName_not_supported ) ;
}
long count = 0 ;
if ( ! IsDisposed ) {
2016-11-10 13:04:39 +00:00
foreach ( var storeRef in _storeRefs ) {
count + = storeRef . Target . Count ;
2016-08-03 10:59:49 +00:00
}
}
return count ;
}
2017-08-21 15:34:15 +00:00
public long GetLastSize ( string regionName = null ) {
if ( regionName ! = null ) {
throw new NotSupportedException ( R . RegionName_not_supported ) ;
}
return _stats . GetLastSize ( ) ;
}
2016-08-03 10:59:49 +00:00
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification="This is assembly is a special case approved by the NetFx API review board")]
public override IDictionary < string , object > GetValues ( IEnumerable < String > keys , string regionName = null ) {
if ( regionName ! = null ) {
throw new NotSupportedException ( R . RegionName_not_supported ) ;
}
if ( keys = = null ) {
throw new ArgumentNullException ( "keys" ) ;
}
Dictionary < string , object > values = null ;
if ( ! IsDisposed ) {
foreach ( string key in keys ) {
if ( key = = null ) {
throw new ArgumentException ( RH . Format ( R . Collection_contains_null_element , "keys" ) ) ;
}
object value = GetInternal ( key , null ) ;
if ( value ! = null ) {
if ( values = = null ) {
values = new Dictionary < string , object > ( ) ;
}
values [ key ] = value ;
}
}
}
return values ;
}
// used when redirecting ASP.NET cache into the MemoryCache. This avoids infinite recursion
// due to the fact that the (ASP.NET) config system uses the cache, and the cache uses the
// config system.
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")]
internal void UpdateConfig ( NameValueCollection config ) {
if ( config = = null ) {
throw new ArgumentNullException ( "config" ) ;
}
if ( ! IsDisposed ) {
_stats . UpdateConfig ( config ) ;
}
}
}
}