// // Copyright (c) 2009 Microsoft Corporation. All rights reserved. // using System; using System.Runtime.Caching.Resources; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text; namespace System.Runtime.Caching { internal sealed class MemoryCacheEntryChangeMonitor : CacheEntryChangeMonitor { // Dev10 909507: use UTC minimum DateTime for error free conversions to DateTimeOffset private static readonly DateTime DATETIME_MINVALUE_UTC = new DateTime(0, DateTimeKind.Utc); private const int MAX_CHAR_COUNT_OF_LONG_CONVERTED_TO_HEXADECIMAL_STRING = 16; private ReadOnlyCollection _keys; private String _regionName; private String _uniqueId; private DateTimeOffset _lastModified; private List _dependencies; private MemoryCacheEntryChangeMonitor() {} // hide default .ctor private void InitDisposableMembers(MemoryCache cache) { bool dispose = true; try { bool hasChanged = false; string uniqueId = null; _dependencies = new List(_keys.Count); if (_keys.Count == 1) { string k = _keys[0]; MemoryCacheEntry entry = cache.GetEntry(k); DateTime utcCreated = DATETIME_MINVALUE_UTC; StartMonitoring(cache, entry, ref hasChanged, ref utcCreated); uniqueId = k + utcCreated.Ticks.ToString("X", CultureInfo.InvariantCulture); _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 = DATETIME_MINVALUE_UTC; StartMonitoring(cache, entry, ref hasChanged, ref utcCreated); sb.Append(key); sb.Append(utcCreated.Ticks.ToString("X", CultureInfo.InvariantCulture)); 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) { if (entry != null) { entry.RemoveDependent(this); } } } } } // // public and internal members // public override ReadOnlyCollection CacheKeys { get { return new ReadOnlyCollection(_keys); } } public override string RegionName { get { return _regionName; } } public override string UniqueId { get { return _uniqueId; } } public override DateTimeOffset LastModified { get { return _lastModified; } } internal List Dependencies { get { return _dependencies; } } internal MemoryCacheEntryChangeMonitor(ReadOnlyCollection keys, String regionName, MemoryCache cache) { Dbg.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); } } }