| File: System\Runtime\Caching\MemoryCacheEntryChangeMonitor.cs | Web Access |
| Project: src\runtime\src\libraries\System.Runtime.Caching\src\System.Runtime.Caching.csproj (System.Runtime.Caching) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.Caching.Resources; using System.Text; namespace System.Runtime.Caching { internal sealed class MemoryCacheEntryChangeMonitor : CacheEntryChangeMonitor { // use UTC minimum DateTime for error free conversions to DateTimeOffset private static readonly DateTime s_DATETIME_MINVALUE_UTC = new DateTime(0, DateTimeKind.Utc); private const int MAX_CHAR_COUNT_OF_LONG_CONVERTED_TO_HEXADECIMAL_STRING = 16; private readonly ReadOnlyCollection<string> _keys; private readonly string _regionName; private string _uniqueId; private DateTimeOffset _lastModified; private List<MemoryCacheEntry> _dependencies; private MemoryCacheEntryChangeMonitor() { } // hide default .ctor private void InitDisposableMembers(MemoryCache cache) { bool dispose = true; try { bool hasChanged = false; string uniqueId = null; _dependencies = new List<MemoryCacheEntry>(_keys.Count); if (_keys.Count == 1) { string k = _keys[0]; MemoryCacheEntry entry = cache.GetEntry(k); DateTime utcCreated = s_DATETIME_MINVALUE_UTC; StartMonitoring(cache, entry, ref hasChanged, ref utcCreated); uniqueId = $"{k}{utcCreated.Ticks:X}"; _lastModified = utcCreated; } else { int capacity = 0; foreach (string key in _keys) { capacity += key.Length + MAX_CHAR_COUNT_OF_LONG_CONVERTED_TO_HEXADECIMAL_STRING; } StringBuilder sb = new StringBuilder(capacity); foreach (string key in _keys) { MemoryCacheEntry entry = cache.GetEntry(key); DateTime utcCreated = s_DATETIME_MINVALUE_UTC; StartMonitoring(cache, entry, ref hasChanged, ref utcCreated); sb.Append(key); sb.Append($"{utcCreated.Ticks:X}"); if (utcCreated > _lastModified) { _lastModified = utcCreated; } } uniqueId = sb.ToString(); } _uniqueId = uniqueId; if (hasChanged) { OnChanged(null); } dispose = false; } finally { InitializationComplete(); if (dispose) { Dispose(); } } } private void StartMonitoring(MemoryCache cache, MemoryCacheEntry entry, ref bool hasChanged, ref DateTime utcCreated) { if (entry != null) { // pass reference to self so the dependency can notify us when it changes entry.AddDependent(cache, this); // add dependency to collection so we can dispose it later _dependencies.Add(entry); // has the entry already changed? if (entry.State != EntryState.AddedToCache) { hasChanged = true; } utcCreated = entry.UtcCreated; } else { // the entry does not exist--set hasChanged to true so the user is notified hasChanged = true; } } // // protected members // protected override void Dispose(bool disposing) { if (disposing) { if (_dependencies != null) { foreach (MemoryCacheEntry entry in _dependencies) { entry?.RemoveDependent(this); } } } } // // public and internal members // public override ReadOnlyCollection<string> CacheKeys { get { return new ReadOnlyCollection<string>(_keys); } } public override string RegionName { get { return _regionName; } } public override string UniqueId { get { return _uniqueId; } } public override DateTimeOffset LastModified { get { return _lastModified; } } internal List<MemoryCacheEntry> Dependencies { get { return _dependencies; } } internal MemoryCacheEntryChangeMonitor(ReadOnlyCollection<string> keys, string regionName, MemoryCache cache) { Debug.Assert(keys != null && keys.Count > 0, "keys != null && keys.Count > 0"); _keys = keys; _regionName = regionName; InitDisposableMembers(cache); } // invoked by a cache entry dependency when it is released from the cache internal void OnCacheEntryReleased() { OnChanged(null); } } }