Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Web / Cache / CacheDependency.cs / 1305376 / CacheDependency.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* * CacheDependency.cs * * Copyright (c) 1998-1999, Microsoft Corporation * */ namespace System.Web.Caching { using System.Collections; using System.Text; using System.IO; using System.Threading; using System.Web.Util; using System.Security.Permissions; using System.Globalization; #if USE_MEMORY_CACHE using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime.Caching; #endif internal interface ICacheDependencyChanged { void DependencyChanged(Object sender, EventArgs e); } ////// // Overhead is 24 bytes + object header public class CacheDependency : IDisposable { #if DBG bool _isUniqueIDInitialized; #endif string _uniqueID; // used by HttpCachePolicy for the ETag object _depFileInfos; // files to monitor for changes, either a DepFileInfo or array of DepFileInfos object _entries; // cache entries we are dependent on, either a string or array of strings ICacheDependencyChanged _objNotify; // Associated object to notify when a change occurs SafeBitVector32 _bits; // status bits for ready, used, changed, disposed DateTime _utcLastModified; // Time of last modified item #if USE_MEMORY_CACHE HostFileChangeMonitor _fileChangeMonitor; CacheEntryChangeMonitor _entryChangeMonitor; #endif static readonly string[] s_stringsEmpty; static readonly CacheEntry[] s_entriesEmpty; static readonly CacheDependency s_dependencyEmpty; static readonly DepFileInfo[] s_depFileInfosEmpty; static readonly TimeSpan FUTURE_FILETIME_BUFFER = new TimeSpan(0, 1, 0); // See VSWhidbey 400917 const int BASE_INIT = 0x01; const int USED = 0x02; const int CHANGED = 0x04; const int BASE_DISPOSED = 0x08; const int WANTS_DISPOSE = 0x10; const int DERIVED_INIT = 0x20; const int DERIVED_DISPOSED = 0x40; internal class DepFileInfo { internal string _filename; internal FileAttributesData _fad; } static CacheDependency() { s_stringsEmpty = new string[0]; s_entriesEmpty = new CacheEntry[0]; s_dependencyEmpty = new CacheDependency(0); s_depFileInfosEmpty = new DepFileInfo[0]; } // creates an empty dependency which is used only by s_dependencyEmpty private CacheDependency(int bogus) { Debug.Assert(s_dependencyEmpty == null, "s_dependencyEmpty == null"); } protected CacheDependency() { Init(true, null, null, null, DateTime.MaxValue); } ///The ///class tracks cache dependencies, which can be files, /// directories, or keys to other objects in the System.Web.Cache.Cache. When an object of this class /// is constructed, it immediately begins monitoring objects on which it is /// dependent for changes. This avoids losing the changes made between the time the /// object to cache is created and the time it is inserted into the /// . /// public CacheDependency(string filename) : this (filename, DateTime.MaxValue) { } public CacheDependency(string filename, DateTime start) { if (filename == null) { return; } DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start); string[] filenames = new string[1] {filename}; Init(true, filenames, null, null, utcStart); } ///Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance /// monitors a file or directory for changes. ////// public CacheDependency(string[] filenames) { Init(true, filenames, null, null, DateTime.MaxValue); } public CacheDependency(string[] filenames, DateTime start) { DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start); Init(true, filenames, null, null, utcStart); } ///Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance monitors an array /// files or directories for changes. ////// public CacheDependency(string[] filenames, string[] cachekeys) { Init(true, filenames, cachekeys, null, DateTime.MaxValue); } public CacheDependency(string[] filenames, string[] cachekeys, DateTime start) { DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start); Init(true, filenames, cachekeys, null, utcStart); } public CacheDependency(string[] filenames, string[] cachekeys, CacheDependency dependency) { Init(true, filenames, cachekeys, dependency, DateTime.MaxValue); } public CacheDependency(string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime start) { DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start); Init(true, filenames, cachekeys, dependency, utcStart); } internal CacheDependency(int dummy, string filename) : this(dummy, filename, DateTime.MaxValue) { } internal CacheDependency(int dummy, string filename, DateTime utcStart) { if (filename == null) { return; } string[] filenames = new string[1] {filename}; Init(false, filenames, null, null, utcStart); } internal CacheDependency(int dummy, string[] filenames) { Init(false, filenames, null, null, DateTime.MaxValue); } internal CacheDependency(int dummy, string[] filenames, DateTime utcStart) { Init(false, filenames, null, null, utcStart); } internal CacheDependency(int dummy, string[] filenames, string[] cachekeys) { Init(false, filenames, cachekeys, null, DateTime.MaxValue); } internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, DateTime utcStart) { Init(false, filenames, cachekeys, null, utcStart); } internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, CacheDependency dependency) { Init(false, filenames, cachekeys, dependency, DateTime.MaxValue); } internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime utcStart) { Init(false, filenames, cachekeys, dependency, utcStart); } #if USE_MEMORY_CACHE void OnChangedCallback(object state) { Debug.Trace("CacheDependencyFileChange", "OnChangedCallback fired"); NotifyDependencyChanged(this, EventArgs.Empty); } void InitForMemoryCache(bool isPublic, string[] filenamesArg, string[] cachekeysArg, CacheDependency dependency, DateTime utcStart) { bool dispose = true; try { MemCache memCache = HttpRuntime.CacheInternal as MemCache; _bits = new SafeBitVector32(0); _utcLastModified = DateTime.MinValue; IListInitializes a new instance of the System.Web.Cache.CacheDependency class. The new instance monitors an /// array files, directories, and cache keys for changes. ///files = filenamesArg; IList keys = cachekeysArg; if (dependency != null) { ReadOnlyCollection filePaths = (dependency._fileChangeMonitor != null) ? dependency._fileChangeMonitor.FilePaths : null; ReadOnlyCollection cacheKeys = (dependency._entryChangeMonitor != null) ? dependency._entryChangeMonitor.CacheKeys : null; if (filePaths != null || filenamesArg != null) { if (filePaths == null) { files = filenamesArg; } else if (filenamesArg == null) { files = filePaths; } else { files = new List (filenamesArg.Length + filePaths.Count); foreach (string f in filenamesArg) { files.Add(f); } foreach (string f in filePaths) { files.Add(f); } } } if (cacheKeys != null || cachekeysArg != null) { if (cacheKeys == null) { keys = cachekeysArg; } else if (cachekeysArg == null) { keys = cacheKeys; } else { keys = new List (cachekeysArg.Length + cacheKeys.Count); foreach (string f in cachekeysArg) { keys.Add(f); } foreach (string f in cacheKeys) { keys.Add(f); } } } } _fileChangeMonitor = (files != null) ? new HostFileChangeMonitor(files) : null; _entryChangeMonitor = (keys != null) ? memCache.CreateCacheEntryChangeMonitor(keys, isPublic) : null; string uniqueId = null; if (_fileChangeMonitor != null) { _utcLastModified = _fileChangeMonitor.LastModified.UtcDateTime; uniqueId = _fileChangeMonitor.UniqueId; _fileChangeMonitor.NotifyOnChanged(new OnChangedCallback(OnChangedCallback)); } if (_entryChangeMonitor != null) { DateTime utcLastModified = _entryChangeMonitor.LastModified.UtcDateTime; if (utcLastModified > _utcLastModified) { _utcLastModified = utcLastModified; } uniqueId += _entryChangeMonitor.UniqueId; _entryChangeMonitor.NotifyOnChanged(new OnChangedCallback(OnChangedCallback)); } _uniqueID = uniqueId; #if DBG _isUniqueIDInitialized = true; #endif // check if file has changed since the start time if (utcStart < DateTime.MaxValue) { if (_utcLastModified > utcStart && !(_utcLastModified - DateTime.UtcNow > FUTURE_FILETIME_BUFFER)) { // See VSWhidbey 400917 _bits[CHANGED] = true; } } _bits[BASE_INIT] = true; if (dependency != null && dependency._bits[CHANGED]) { _bits[CHANGED] = true; } if (_bits[WANTS_DISPOSE] || _bits[CHANGED]) { Debug.Trace("CacheDependencyInit", "WANTS_DISPOSE or CHANGED. InitForMemoryCache calling DisposeInternal"); DisposeInternal(); } dispose = false; } finally { if (dispose) { _bits[BASE_INIT] = true; Debug.Trace("CacheDependencyInit", "\n\nERROR in CacheDependency.InitForMemoryCache, calling DisposeInternal"); DisposeInternal(); } } } #endif void Init(bool isPublic, string[] filenamesArg, string[] cachekeysArg, CacheDependency dependency, DateTime utcStart) { #if USE_MEMORY_CACHE if (CacheInternal.UseMemoryCache) { InitForMemoryCache(isPublic, filenamesArg, cachekeysArg, dependency, utcStart); return; } #endif DepFileInfo[] depFileInfos = s_depFileInfosEmpty; CacheEntry[] depEntries = s_entriesEmpty; string [] filenames, cachekeys; CacheInternal cacheInternal; _bits = new SafeBitVector32(0); // copy array argument contents so they can't be changed beneath us if (filenamesArg != null) { filenames = (string []) filenamesArg.Clone(); } else { filenames = null; } if (cachekeysArg != null) { cachekeys = (string []) cachekeysArg.Clone(); } else { cachekeys = null; } _utcLastModified = DateTime.MinValue; try { // validate filenames array if (filenames == null) { filenames = s_stringsEmpty; } else { foreach (string f in filenames) { if (f == null) { throw new ArgumentNullException("filenamesArg"); } // demand PathDiscovery if public if (isPublic) { InternalSecurityPermissions.PathDiscovery(f).Demand(); } } } if (cachekeys == null) { cachekeys = s_stringsEmpty; } else { // validate cachekeys array foreach (string k in cachekeys) { if (k == null) { throw new ArgumentNullException("cachekeysArg"); } } } // copy all parts of another dependency if provided if (dependency == null) { dependency = s_dependencyEmpty; } else { if (dependency.GetType() != s_dependencyEmpty.GetType()) { throw new ArgumentException(SR.GetString(SR.Invalid_Dependency_Type)); } // Copy the parts of the dependency we need before // we reference them, as the dependency can change // underneath us. object d_depFileInfos = dependency._depFileInfos; object d_entries = dependency._entries; DateTime d_lastModified = dependency._utcLastModified; // if the dependency we're copying has changed, we're done if (dependency._bits[CHANGED]) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } // copy depFileInfos if (d_depFileInfos != null) { if (d_depFileInfos is DepFileInfo) { depFileInfos = new DepFileInfo[1] {(DepFileInfo) d_depFileInfos}; } else { depFileInfos = (DepFileInfo[]) (d_depFileInfos); } // verify that the object was fully constructed // and that we have permission to discover the file foreach (DepFileInfo depFileInfo in depFileInfos) { string f = depFileInfo._filename; if (f == null) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } // demand PathDiscovery if public if (isPublic) { InternalSecurityPermissions.PathDiscovery(f).Demand(); } } } // copy cache entries if (d_entries != null) { if (d_entries is CacheEntry) { depEntries = new CacheEntry[1] {(CacheEntry) (d_entries)}; } else { depEntries = (CacheEntry[]) (d_entries); // verify that the object was fully constructed foreach (CacheEntry entry in depEntries) { if (entry == null) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } } } } _utcLastModified = d_lastModified; } // Monitor files for changes int lenMyDepFileInfos = depFileInfos.Length + filenames.Length; if (lenMyDepFileInfos > 0) { DepFileInfo[] myDepFileInfos = new DepFileInfo[lenMyDepFileInfos]; FileChangeEventHandler handler = new FileChangeEventHandler(this.FileChange); FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor; int i; for (i = 0; i < lenMyDepFileInfos; i++) { myDepFileInfos[i] = new DepFileInfo(); } // monitor files from the existing dependency // note that we don't check for start times in the existing dependency i = 0; foreach (DepFileInfo depFileInfo in depFileInfos) { string f = depFileInfo._filename; fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad); myDepFileInfos[i]._filename = f; i++; } // monitor new files DateTime utcNow = DateTime.MinValue; foreach (string f in filenames) { DateTime utcLastWrite = fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad); myDepFileInfos[i]._filename = f; i++; if (utcLastWrite > _utcLastModified) { _utcLastModified = utcLastWrite; } // check if file has changed since the start time if (utcStart < DateTime.MaxValue) { if (utcNow == DateTime.MinValue) { utcNow = DateTime.UtcNow; } Debug.Trace("CacheDependencyInit", "file=" + f + "; utcStart=" + utcStart + "; utcLastWrite=" + utcLastWrite); if (utcLastWrite >= utcStart && !(utcLastWrite - utcNow > FUTURE_FILETIME_BUFFER)) { // See VSWhidbey 400917 Debug.Trace("CacheDependencyInit", "changes occurred since start time for file " + f); _bits[CHANGED] = true; break; } } } if (myDepFileInfos.Length == 1) { _depFileInfos = myDepFileInfos[0]; } else { _depFileInfos = myDepFileInfos; } } // Monitor other cache entries for changes int lenMyEntries = depEntries.Length + cachekeys.Length; if (lenMyEntries > 0 && !_bits[CHANGED]) { CacheEntry[] myEntries = new CacheEntry[lenMyEntries]; // Monitor entries from the existing cache dependency int i = 0; foreach (CacheEntry entry in depEntries) { entry.AddCacheDependencyNotify(this); myEntries[i++] = entry; } // Monitor new entries specified for this depenedency // Entries must be added to cache, and created before the startTime cacheInternal = HttpRuntime.CacheInternal; foreach (string k in cachekeys) { CacheEntry entry = (CacheEntry) cacheInternal.DoGet(isPublic, k, CacheGetOptions.ReturnCacheEntry); if (entry != null) { entry.AddCacheDependencyNotify(this); myEntries[i++] = entry; if (entry.UtcCreated > _utcLastModified) { _utcLastModified = entry.UtcCreated; } if ( entry.State != CacheEntry.EntryState.AddedToCache || entry.UtcCreated > utcStart) { #if DBG if (entry.State != CacheEntry.EntryState.AddedToCache) { Debug.Trace("CacheDependencyInit", "Entry is not in cache, considered changed:" + k); } else { Debug.Trace("CacheDependencyInit", "Changes occurred to entry since start time:" + k); } #endif _bits[CHANGED] = true; break; } } else { Debug.Trace("CacheDependencyInit", "Cache item not found to create dependency on:" + k); _bits[CHANGED] = true; break; } } if (myEntries.Length == 1) { _entries = myEntries[0]; } else { _entries = myEntries; } } _bits[BASE_INIT] = true; if (dependency._bits[CHANGED]) { _bits[CHANGED] = true; } if (_bits[WANTS_DISPOSE] || _bits[CHANGED]) { DisposeInternal(); } Debug.Assert(_objNotify == null, "_objNotify == null"); } catch { // derived constructor will not execute due to the throw, // so we just force a dispose on ourselves _bits[BASE_INIT] = true; DisposeInternal(); throw; } finally { InitUniqueID(); } } public void Dispose() { // Set this bit just in case our derived ctor forgot to call FinishInit() _bits[DERIVED_INIT] = true; if (Use()) { // Do the dispose only if the cache has not already used us DisposeInternal(); } } protected internal void FinishInit() { _bits[DERIVED_INIT] = true; if (_bits[WANTS_DISPOSE]) { DisposeInternal(); } } /* * Shutdown all dependency monitoring and firing of NotifyDependencyChanged notification. */ internal void DisposeInternal() { _bits[WANTS_DISPOSE] = true; if (_bits[DERIVED_INIT]) { if (_bits.ChangeValue(DERIVED_DISPOSED, true)) { // Dispose derived classes DependencyDispose(); } } if (_bits[BASE_INIT]) { if (_bits.ChangeValue(BASE_DISPOSED, true)) { // Dispose ourself DisposeOurself(); } } } // Allow derived class to dispose itself protected virtual void DependencyDispose() { // We do our own dispose work in DisposeOurself, so that // we don't rely on derived classes calling their base // DependencyDispose for us to function correctly. } void DisposeOurself() { // guarantee that we execute only once if an exception // is thrown from this function by nulling fields before // we access them object l_depFileInfos = _depFileInfos; object l_entries = _entries; _objNotify = null; _depFileInfos = null; _entries = null; // stop monitoring files if (l_depFileInfos != null) { FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor; DepFileInfo oneDepFileInfo = l_depFileInfos as DepFileInfo; if (oneDepFileInfo != null) { fmon.StopMonitoringPath(oneDepFileInfo._filename, this); } else { DepFileInfo[] depFileInfos = (DepFileInfo[]) l_depFileInfos; foreach (DepFileInfo depFileInfo in depFileInfos) { // ensure that we handle partially contructed // objects by checking filename for null string filename = depFileInfo._filename; if (filename != null) { fmon.StopMonitoringPath(filename, this); } } } } // stop monitoring cache items if (l_entries != null) { CacheEntry oneEntry = l_entries as CacheEntry; if (oneEntry != null) { oneEntry.RemoveCacheDependencyNotify(this); } else { CacheEntry[] entries = (CacheEntry[]) l_entries; foreach (CacheEntry entry in entries) { // ensure that we handle partially contructed // objects by checking entry for null if (entry != null) { entry.RemoveCacheDependencyNotify(this); } } } } #if USE_MEMORY_CACHE if (_fileChangeMonitor != null) { _fileChangeMonitor.Dispose(); } if (_entryChangeMonitor != null) { _entryChangeMonitor.Dispose(); } #endif } // allow the first user to declare ownership internal bool Use() { return _bits.ChangeValue(USED, true); } // // Has a dependency changed? // public bool HasChanged { get {return _bits[CHANGED];} } public DateTime UtcLastModified { get { return _utcLastModified; } } protected void SetUtcLastModified(DateTime utcLastModified) { _utcLastModified = utcLastModified; } // // Add/remove an NotifyDependencyChanged notification. // internal void SetCacheDependencyChanged(ICacheDependencyChanged objNotify) { Debug.Assert(_objNotify == null, "_objNotify == null"); // Set this bit just in case our derived ctor forgot to call FinishInit() _bits[DERIVED_INIT] = true; if (!_bits[BASE_DISPOSED]) { _objNotify = objNotify; } } internal void AppendFileUniqueId(DepFileInfo depFileInfo, StringBuilder sb) { FileAttributesData fad = depFileInfo._fad; if (fad == null) { fad = FileAttributesData.NonExistantAttributesData; } sb.Append(depFileInfo._filename); sb.Append(fad.UtcLastWriteTime.Ticks.ToString("d", NumberFormatInfo.InvariantInfo)); sb.Append(fad.FileSize.ToString(CultureInfo.InvariantCulture)); } void InitUniqueID() { StringBuilder sb = null; object l_depFileInfos, l_entries; #if !FEATURE_PAL // no File Change Monitoring // get unique id from files l_depFileInfos = _depFileInfos; if (l_depFileInfos != null) { DepFileInfo oneDepFileInfo = l_depFileInfos as DepFileInfo; if (oneDepFileInfo != null) { sb = new StringBuilder(); AppendFileUniqueId(oneDepFileInfo, sb); } else { DepFileInfo[] depFileInfos = (DepFileInfo[]) l_depFileInfos; foreach (DepFileInfo depFileInfo in depFileInfos) { // ensure that we handle partially contructed // objects by checking filename for null if (depFileInfo._filename != null) { if (sb == null) sb = new StringBuilder(); AppendFileUniqueId(depFileInfo, sb); } } } } #endif // !FEATURE_PAL // get unique id from cache entries l_entries = _entries; if (l_entries != null) { CacheEntry oneEntry = l_entries as CacheEntry; if (oneEntry != null) { if (sb == null) sb = new StringBuilder(); sb.Append(oneEntry.Key); sb.Append(oneEntry.UtcCreated.Ticks.ToString(CultureInfo.InvariantCulture)); } else { CacheEntry[] entries = (CacheEntry[]) l_entries; foreach (CacheEntry entry in entries) { // ensure that we handle partially contructed // objects by checking entry for null if (entry != null) { if (sb == null) sb = new StringBuilder(); sb.Append(entry.Key); sb.Append(entry.UtcCreated.Ticks.ToString(CultureInfo.InvariantCulture)); } } } } if (sb != null) _uniqueID = sb.ToString(); #if DBG _isUniqueIDInitialized = true; #endif } public virtual string GetUniqueID() { #if DBG Debug.Assert(_isUniqueIDInitialized == true, "_isUniqueIDInitialized == true"); #endif return _uniqueID; } // // Return the cacheEntries monitored by this dependency // internal CacheEntry[] CacheEntries { get { if (_entries == null) { return null; } CacheEntry oneEntry = _entries as CacheEntry; if (oneEntry != null) { return new CacheEntry[1] {oneEntry}; } return (CacheEntry[]) _entries; } } // // This object has changed, so fire the NotifyDependencyChanged event. // We only allow this event to be fired once. // protected void NotifyDependencyChanged(Object sender, EventArgs e) { if (_bits.ChangeValue(CHANGED, true)) { _utcLastModified = DateTime.UtcNow; ICacheDependencyChanged objNotify = _objNotify; if (objNotify != null && !_bits[BASE_DISPOSED]) { Debug.Trace("CacheDependencyNotifyDependencyChanged", "change occurred"); objNotify.DependencyChanged(sender, e); } DisposeInternal(); } } // // ItemRemoved is called when a cache entry we are monitoring has been removed. // internal void ItemRemoved() { NotifyDependencyChanged(this, EventArgs.Empty); } // // FileChange is called when a file we are monitoring has changed. // void FileChange(Object sender, FileChangeEvent e) { Debug.Trace("CacheDependencyFileChange", "FileChange file=" + e.FileName + ";Action=" + e.Action); NotifyDependencyChanged(sender, e); } // // This will examine the dependency and determine if it's ONLY a file dependency or not // internal virtual bool IsFileDependency() { #if USE_MEMORY_CACHE if (CacheInternal.UseMemoryCache) { if (_entryChangeMonitor != null) { return false; } if (_fileChangeMonitor != null) { return true; } return false; } #endif object depInfos, l_entries; // Check and see if we are dependent on any cache entries l_entries = _entries; if (l_entries != null) { CacheEntry oneEntry = l_entries as CacheEntry; if (oneEntry != null) { return false; } else { CacheEntry[] entries = (CacheEntry[]) l_entries; if (entries != null && entries.Length > 0) { return false; } } } depInfos = _depFileInfos; if (depInfos != null) { DepFileInfo oneDepFileInfo = depInfos as DepFileInfo; if (oneDepFileInfo != null) { return true; } else { DepFileInfo[] depFileInfos = (DepFileInfo[]) depInfos; if (depFileInfos != null && depFileInfos.Length > 0) { return true; } } } return false; } // // This method will return only the file dependencies from this dependency // internal virtual string[] GetFileDependencies() { #if USE_MEMORY_CACHE if (CacheInternal.UseMemoryCache) { if (_fileChangeMonitor != null) { ReadOnlyCollection paths = _fileChangeMonitor.FilePaths; if (paths != null && paths.Count > 0) { string[] aryPaths = new string[paths.Count]; for (int i = 0; i < aryPaths.Length; i++) { aryPaths[i] = paths[i]; } return aryPaths; } } return null; } #endif object depInfos = _depFileInfos; if (depInfos != null) { DepFileInfo oneDepFileInfo = depInfos as DepFileInfo; if (oneDepFileInfo != null) { return new string[] {oneDepFileInfo._filename}; } else { DepFileInfo[] depFileInfos = (DepFileInfo[]) depInfos; string[] names = new string[depFileInfos.Length]; for (int i = 0; i < depFileInfos.Length; i++) { names[i] = depFileInfos[i]._filename; } return names; } } return null; } } public sealed class AggregateCacheDependency : CacheDependency, ICacheDependencyChanged { ArrayList _dependencies; bool _disposed; public AggregateCacheDependency() { // The ctor of every class derived from CacheDependency must call this. FinishInit(); } public void Add(params CacheDependency [] dependencies) { DateTime utcLastModified = DateTime.MinValue; if (dependencies == null) { throw new ArgumentNullException("dependencies"); } // copy array argument contents so they can't be changed beneath us dependencies = (CacheDependency []) dependencies.Clone(); // validate contents foreach (CacheDependency d in dependencies) { if (d == null) { throw new ArgumentNullException("dependencies"); } if (!d.Use()) { throw new InvalidOperationException(SR.GetString(SR.Cache_dependency_used_more_that_once)); } } // add dependencies, and check if any have changed bool hasChanged = false; lock (this) { if (!_disposed) { if (_dependencies == null) { _dependencies = new ArrayList(); } _dependencies.AddRange(dependencies); foreach (CacheDependency d in dependencies) { d.SetCacheDependencyChanged(this); if (d.UtcLastModified > utcLastModified) { utcLastModified = d.UtcLastModified; } if (d.HasChanged) { hasChanged = true; break; } } } } SetUtcLastModified(utcLastModified); // if a dependency has changed, notify others that we have changed. if (hasChanged) { NotifyDependencyChanged(this, EventArgs.Empty); } } // Dispose our dependencies. Note that the call to this // function is thread safe. protected override void DependencyDispose() { CacheDependency[] dependencies = null; lock (this) { _disposed = true; if (_dependencies != null) { dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency)); _dependencies = null; } } if (dependencies != null) { foreach (CacheDependency d in dependencies) { d.DisposeInternal(); } } } // Forward call from the aggregate to the CacheEntry /// void ICacheDependencyChanged.DependencyChanged(Object sender, EventArgs e) { NotifyDependencyChanged(sender, e); } public override string GetUniqueID() { StringBuilder sb = null; CacheDependency[] dependencies = null; //VSWhidbey 354570: return null if this AggregateCacheDependency cannot otherwise return a unique ID if (_dependencies == null) { return null; } lock (this) { if (_dependencies != null) { dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency)); } } if (dependencies != null) { foreach (CacheDependency dependency in dependencies) { string id = dependency.GetUniqueID(); if (id == null) { // When AggregateCacheDependency contains a dependency for which GetUniqueID() returns null, // it should return null itself. This is because it can no longer generate a UniqueID that // is guaranteed to be different when any of the dependencies change. return null; } if (sb == null) { sb = new StringBuilder(); } sb.Append(id); } } return sb != null ? sb.ToString() : null; } internal CacheDependency[] GetDependencyArray() { CacheDependency[] dependencies = null; lock (this) { if (_dependencies != null) { dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency)); } } return dependencies; } // // This will examine the dependencies and only return true if ALL dependencies are file dependencies // internal override bool IsFileDependency() { CacheDependency[] dependencies = null; dependencies = GetDependencyArray(); if (dependencies == null) { return false; } foreach (CacheDependency d in dependencies) { // We should only check if the type is either CacheDependency or the Aggregate. // Anything else, we can't guarantee that it's a file only dependency. if ( ! object.ReferenceEquals(d.GetType(), typeof(CacheDependency)) && ! object.ReferenceEquals(d.GetType(), typeof(AggregateCacheDependency)) ) { return false; } if (! d.IsFileDependency()) { return false; } } return true; } // // This method will return only the file dependencies from this dependency // internal override string[] GetFileDependencies() { ArrayList fileNames = null; CacheDependency[] dependencies = null; dependencies = GetDependencyArray(); if (dependencies == null) { return null; } foreach (CacheDependency d in dependencies) { // Check if the type is either CacheDependency or an Aggregate; // for anything else, we can't guarantee it's a file only dependency. if (object.ReferenceEquals(d.GetType(), typeof(CacheDependency)) || object.ReferenceEquals(d.GetType(), typeof(AggregateCacheDependency))) { string[] tmpFileNames = d.GetFileDependencies(); if (tmpFileNames != null) { if (fileNames == null) { fileNames = new ArrayList(); } fileNames.AddRange(tmpFileNames); } } } if (fileNames != null) { return (string[])fileNames.ToArray(typeof(string)); } else { return null; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* * CacheDependency.cs * * Copyright (c) 1998-1999, Microsoft Corporation * */ namespace System.Web.Caching { using System.Collections; using System.Text; using System.IO; using System.Threading; using System.Web.Util; using System.Security.Permissions; using System.Globalization; #if USE_MEMORY_CACHE using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime.Caching; #endif internal interface ICacheDependencyChanged { void DependencyChanged(Object sender, EventArgs e); } ////// // Overhead is 24 bytes + object header public class CacheDependency : IDisposable { #if DBG bool _isUniqueIDInitialized; #endif string _uniqueID; // used by HttpCachePolicy for the ETag object _depFileInfos; // files to monitor for changes, either a DepFileInfo or array of DepFileInfos object _entries; // cache entries we are dependent on, either a string or array of strings ICacheDependencyChanged _objNotify; // Associated object to notify when a change occurs SafeBitVector32 _bits; // status bits for ready, used, changed, disposed DateTime _utcLastModified; // Time of last modified item #if USE_MEMORY_CACHE HostFileChangeMonitor _fileChangeMonitor; CacheEntryChangeMonitor _entryChangeMonitor; #endif static readonly string[] s_stringsEmpty; static readonly CacheEntry[] s_entriesEmpty; static readonly CacheDependency s_dependencyEmpty; static readonly DepFileInfo[] s_depFileInfosEmpty; static readonly TimeSpan FUTURE_FILETIME_BUFFER = new TimeSpan(0, 1, 0); // See VSWhidbey 400917 const int BASE_INIT = 0x01; const int USED = 0x02; const int CHANGED = 0x04; const int BASE_DISPOSED = 0x08; const int WANTS_DISPOSE = 0x10; const int DERIVED_INIT = 0x20; const int DERIVED_DISPOSED = 0x40; internal class DepFileInfo { internal string _filename; internal FileAttributesData _fad; } static CacheDependency() { s_stringsEmpty = new string[0]; s_entriesEmpty = new CacheEntry[0]; s_dependencyEmpty = new CacheDependency(0); s_depFileInfosEmpty = new DepFileInfo[0]; } // creates an empty dependency which is used only by s_dependencyEmpty private CacheDependency(int bogus) { Debug.Assert(s_dependencyEmpty == null, "s_dependencyEmpty == null"); } protected CacheDependency() { Init(true, null, null, null, DateTime.MaxValue); } ///The ///class tracks cache dependencies, which can be files, /// directories, or keys to other objects in the System.Web.Cache.Cache. When an object of this class /// is constructed, it immediately begins monitoring objects on which it is /// dependent for changes. This avoids losing the changes made between the time the /// object to cache is created and the time it is inserted into the /// . /// public CacheDependency(string filename) : this (filename, DateTime.MaxValue) { } public CacheDependency(string filename, DateTime start) { if (filename == null) { return; } DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start); string[] filenames = new string[1] {filename}; Init(true, filenames, null, null, utcStart); } ///Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance /// monitors a file or directory for changes. ////// public CacheDependency(string[] filenames) { Init(true, filenames, null, null, DateTime.MaxValue); } public CacheDependency(string[] filenames, DateTime start) { DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start); Init(true, filenames, null, null, utcStart); } ///Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance monitors an array /// files or directories for changes. ////// public CacheDependency(string[] filenames, string[] cachekeys) { Init(true, filenames, cachekeys, null, DateTime.MaxValue); } public CacheDependency(string[] filenames, string[] cachekeys, DateTime start) { DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start); Init(true, filenames, cachekeys, null, utcStart); } public CacheDependency(string[] filenames, string[] cachekeys, CacheDependency dependency) { Init(true, filenames, cachekeys, dependency, DateTime.MaxValue); } public CacheDependency(string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime start) { DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start); Init(true, filenames, cachekeys, dependency, utcStart); } internal CacheDependency(int dummy, string filename) : this(dummy, filename, DateTime.MaxValue) { } internal CacheDependency(int dummy, string filename, DateTime utcStart) { if (filename == null) { return; } string[] filenames = new string[1] {filename}; Init(false, filenames, null, null, utcStart); } internal CacheDependency(int dummy, string[] filenames) { Init(false, filenames, null, null, DateTime.MaxValue); } internal CacheDependency(int dummy, string[] filenames, DateTime utcStart) { Init(false, filenames, null, null, utcStart); } internal CacheDependency(int dummy, string[] filenames, string[] cachekeys) { Init(false, filenames, cachekeys, null, DateTime.MaxValue); } internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, DateTime utcStart) { Init(false, filenames, cachekeys, null, utcStart); } internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, CacheDependency dependency) { Init(false, filenames, cachekeys, dependency, DateTime.MaxValue); } internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime utcStart) { Init(false, filenames, cachekeys, dependency, utcStart); } #if USE_MEMORY_CACHE void OnChangedCallback(object state) { Debug.Trace("CacheDependencyFileChange", "OnChangedCallback fired"); NotifyDependencyChanged(this, EventArgs.Empty); } void InitForMemoryCache(bool isPublic, string[] filenamesArg, string[] cachekeysArg, CacheDependency dependency, DateTime utcStart) { bool dispose = true; try { MemCache memCache = HttpRuntime.CacheInternal as MemCache; _bits = new SafeBitVector32(0); _utcLastModified = DateTime.MinValue; IListInitializes a new instance of the System.Web.Cache.CacheDependency class. The new instance monitors an /// array files, directories, and cache keys for changes. ///files = filenamesArg; IList keys = cachekeysArg; if (dependency != null) { ReadOnlyCollection filePaths = (dependency._fileChangeMonitor != null) ? dependency._fileChangeMonitor.FilePaths : null; ReadOnlyCollection cacheKeys = (dependency._entryChangeMonitor != null) ? dependency._entryChangeMonitor.CacheKeys : null; if (filePaths != null || filenamesArg != null) { if (filePaths == null) { files = filenamesArg; } else if (filenamesArg == null) { files = filePaths; } else { files = new List (filenamesArg.Length + filePaths.Count); foreach (string f in filenamesArg) { files.Add(f); } foreach (string f in filePaths) { files.Add(f); } } } if (cacheKeys != null || cachekeysArg != null) { if (cacheKeys == null) { keys = cachekeysArg; } else if (cachekeysArg == null) { keys = cacheKeys; } else { keys = new List (cachekeysArg.Length + cacheKeys.Count); foreach (string f in cachekeysArg) { keys.Add(f); } foreach (string f in cacheKeys) { keys.Add(f); } } } } _fileChangeMonitor = (files != null) ? new HostFileChangeMonitor(files) : null; _entryChangeMonitor = (keys != null) ? memCache.CreateCacheEntryChangeMonitor(keys, isPublic) : null; string uniqueId = null; if (_fileChangeMonitor != null) { _utcLastModified = _fileChangeMonitor.LastModified.UtcDateTime; uniqueId = _fileChangeMonitor.UniqueId; _fileChangeMonitor.NotifyOnChanged(new OnChangedCallback(OnChangedCallback)); } if (_entryChangeMonitor != null) { DateTime utcLastModified = _entryChangeMonitor.LastModified.UtcDateTime; if (utcLastModified > _utcLastModified) { _utcLastModified = utcLastModified; } uniqueId += _entryChangeMonitor.UniqueId; _entryChangeMonitor.NotifyOnChanged(new OnChangedCallback(OnChangedCallback)); } _uniqueID = uniqueId; #if DBG _isUniqueIDInitialized = true; #endif // check if file has changed since the start time if (utcStart < DateTime.MaxValue) { if (_utcLastModified > utcStart && !(_utcLastModified - DateTime.UtcNow > FUTURE_FILETIME_BUFFER)) { // See VSWhidbey 400917 _bits[CHANGED] = true; } } _bits[BASE_INIT] = true; if (dependency != null && dependency._bits[CHANGED]) { _bits[CHANGED] = true; } if (_bits[WANTS_DISPOSE] || _bits[CHANGED]) { Debug.Trace("CacheDependencyInit", "WANTS_DISPOSE or CHANGED. InitForMemoryCache calling DisposeInternal"); DisposeInternal(); } dispose = false; } finally { if (dispose) { _bits[BASE_INIT] = true; Debug.Trace("CacheDependencyInit", "\n\nERROR in CacheDependency.InitForMemoryCache, calling DisposeInternal"); DisposeInternal(); } } } #endif void Init(bool isPublic, string[] filenamesArg, string[] cachekeysArg, CacheDependency dependency, DateTime utcStart) { #if USE_MEMORY_CACHE if (CacheInternal.UseMemoryCache) { InitForMemoryCache(isPublic, filenamesArg, cachekeysArg, dependency, utcStart); return; } #endif DepFileInfo[] depFileInfos = s_depFileInfosEmpty; CacheEntry[] depEntries = s_entriesEmpty; string [] filenames, cachekeys; CacheInternal cacheInternal; _bits = new SafeBitVector32(0); // copy array argument contents so they can't be changed beneath us if (filenamesArg != null) { filenames = (string []) filenamesArg.Clone(); } else { filenames = null; } if (cachekeysArg != null) { cachekeys = (string []) cachekeysArg.Clone(); } else { cachekeys = null; } _utcLastModified = DateTime.MinValue; try { // validate filenames array if (filenames == null) { filenames = s_stringsEmpty; } else { foreach (string f in filenames) { if (f == null) { throw new ArgumentNullException("filenamesArg"); } // demand PathDiscovery if public if (isPublic) { InternalSecurityPermissions.PathDiscovery(f).Demand(); } } } if (cachekeys == null) { cachekeys = s_stringsEmpty; } else { // validate cachekeys array foreach (string k in cachekeys) { if (k == null) { throw new ArgumentNullException("cachekeysArg"); } } } // copy all parts of another dependency if provided if (dependency == null) { dependency = s_dependencyEmpty; } else { if (dependency.GetType() != s_dependencyEmpty.GetType()) { throw new ArgumentException(SR.GetString(SR.Invalid_Dependency_Type)); } // Copy the parts of the dependency we need before // we reference them, as the dependency can change // underneath us. object d_depFileInfos = dependency._depFileInfos; object d_entries = dependency._entries; DateTime d_lastModified = dependency._utcLastModified; // if the dependency we're copying has changed, we're done if (dependency._bits[CHANGED]) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } // copy depFileInfos if (d_depFileInfos != null) { if (d_depFileInfos is DepFileInfo) { depFileInfos = new DepFileInfo[1] {(DepFileInfo) d_depFileInfos}; } else { depFileInfos = (DepFileInfo[]) (d_depFileInfos); } // verify that the object was fully constructed // and that we have permission to discover the file foreach (DepFileInfo depFileInfo in depFileInfos) { string f = depFileInfo._filename; if (f == null) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } // demand PathDiscovery if public if (isPublic) { InternalSecurityPermissions.PathDiscovery(f).Demand(); } } } // copy cache entries if (d_entries != null) { if (d_entries is CacheEntry) { depEntries = new CacheEntry[1] {(CacheEntry) (d_entries)}; } else { depEntries = (CacheEntry[]) (d_entries); // verify that the object was fully constructed foreach (CacheEntry entry in depEntries) { if (entry == null) { _bits[CHANGED] = true; // There is nothing to dispose because we haven't started // monitoring anything yet. But we call DisposeInternal in // order to set the WANTS_DISPOSE bit. DisposeInternal(); return; } } } } _utcLastModified = d_lastModified; } // Monitor files for changes int lenMyDepFileInfos = depFileInfos.Length + filenames.Length; if (lenMyDepFileInfos > 0) { DepFileInfo[] myDepFileInfos = new DepFileInfo[lenMyDepFileInfos]; FileChangeEventHandler handler = new FileChangeEventHandler(this.FileChange); FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor; int i; for (i = 0; i < lenMyDepFileInfos; i++) { myDepFileInfos[i] = new DepFileInfo(); } // monitor files from the existing dependency // note that we don't check for start times in the existing dependency i = 0; foreach (DepFileInfo depFileInfo in depFileInfos) { string f = depFileInfo._filename; fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad); myDepFileInfos[i]._filename = f; i++; } // monitor new files DateTime utcNow = DateTime.MinValue; foreach (string f in filenames) { DateTime utcLastWrite = fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad); myDepFileInfos[i]._filename = f; i++; if (utcLastWrite > _utcLastModified) { _utcLastModified = utcLastWrite; } // check if file has changed since the start time if (utcStart < DateTime.MaxValue) { if (utcNow == DateTime.MinValue) { utcNow = DateTime.UtcNow; } Debug.Trace("CacheDependencyInit", "file=" + f + "; utcStart=" + utcStart + "; utcLastWrite=" + utcLastWrite); if (utcLastWrite >= utcStart && !(utcLastWrite - utcNow > FUTURE_FILETIME_BUFFER)) { // See VSWhidbey 400917 Debug.Trace("CacheDependencyInit", "changes occurred since start time for file " + f); _bits[CHANGED] = true; break; } } } if (myDepFileInfos.Length == 1) { _depFileInfos = myDepFileInfos[0]; } else { _depFileInfos = myDepFileInfos; } } // Monitor other cache entries for changes int lenMyEntries = depEntries.Length + cachekeys.Length; if (lenMyEntries > 0 && !_bits[CHANGED]) { CacheEntry[] myEntries = new CacheEntry[lenMyEntries]; // Monitor entries from the existing cache dependency int i = 0; foreach (CacheEntry entry in depEntries) { entry.AddCacheDependencyNotify(this); myEntries[i++] = entry; } // Monitor new entries specified for this depenedency // Entries must be added to cache, and created before the startTime cacheInternal = HttpRuntime.CacheInternal; foreach (string k in cachekeys) { CacheEntry entry = (CacheEntry) cacheInternal.DoGet(isPublic, k, CacheGetOptions.ReturnCacheEntry); if (entry != null) { entry.AddCacheDependencyNotify(this); myEntries[i++] = entry; if (entry.UtcCreated > _utcLastModified) { _utcLastModified = entry.UtcCreated; } if ( entry.State != CacheEntry.EntryState.AddedToCache || entry.UtcCreated > utcStart) { #if DBG if (entry.State != CacheEntry.EntryState.AddedToCache) { Debug.Trace("CacheDependencyInit", "Entry is not in cache, considered changed:" + k); } else { Debug.Trace("CacheDependencyInit", "Changes occurred to entry since start time:" + k); } #endif _bits[CHANGED] = true; break; } } else { Debug.Trace("CacheDependencyInit", "Cache item not found to create dependency on:" + k); _bits[CHANGED] = true; break; } } if (myEntries.Length == 1) { _entries = myEntries[0]; } else { _entries = myEntries; } } _bits[BASE_INIT] = true; if (dependency._bits[CHANGED]) { _bits[CHANGED] = true; } if (_bits[WANTS_DISPOSE] || _bits[CHANGED]) { DisposeInternal(); } Debug.Assert(_objNotify == null, "_objNotify == null"); } catch { // derived constructor will not execute due to the throw, // so we just force a dispose on ourselves _bits[BASE_INIT] = true; DisposeInternal(); throw; } finally { InitUniqueID(); } } public void Dispose() { // Set this bit just in case our derived ctor forgot to call FinishInit() _bits[DERIVED_INIT] = true; if (Use()) { // Do the dispose only if the cache has not already used us DisposeInternal(); } } protected internal void FinishInit() { _bits[DERIVED_INIT] = true; if (_bits[WANTS_DISPOSE]) { DisposeInternal(); } } /* * Shutdown all dependency monitoring and firing of NotifyDependencyChanged notification. */ internal void DisposeInternal() { _bits[WANTS_DISPOSE] = true; if (_bits[DERIVED_INIT]) { if (_bits.ChangeValue(DERIVED_DISPOSED, true)) { // Dispose derived classes DependencyDispose(); } } if (_bits[BASE_INIT]) { if (_bits.ChangeValue(BASE_DISPOSED, true)) { // Dispose ourself DisposeOurself(); } } } // Allow derived class to dispose itself protected virtual void DependencyDispose() { // We do our own dispose work in DisposeOurself, so that // we don't rely on derived classes calling their base // DependencyDispose for us to function correctly. } void DisposeOurself() { // guarantee that we execute only once if an exception // is thrown from this function by nulling fields before // we access them object l_depFileInfos = _depFileInfos; object l_entries = _entries; _objNotify = null; _depFileInfos = null; _entries = null; // stop monitoring files if (l_depFileInfos != null) { FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor; DepFileInfo oneDepFileInfo = l_depFileInfos as DepFileInfo; if (oneDepFileInfo != null) { fmon.StopMonitoringPath(oneDepFileInfo._filename, this); } else { DepFileInfo[] depFileInfos = (DepFileInfo[]) l_depFileInfos; foreach (DepFileInfo depFileInfo in depFileInfos) { // ensure that we handle partially contructed // objects by checking filename for null string filename = depFileInfo._filename; if (filename != null) { fmon.StopMonitoringPath(filename, this); } } } } // stop monitoring cache items if (l_entries != null) { CacheEntry oneEntry = l_entries as CacheEntry; if (oneEntry != null) { oneEntry.RemoveCacheDependencyNotify(this); } else { CacheEntry[] entries = (CacheEntry[]) l_entries; foreach (CacheEntry entry in entries) { // ensure that we handle partially contructed // objects by checking entry for null if (entry != null) { entry.RemoveCacheDependencyNotify(this); } } } } #if USE_MEMORY_CACHE if (_fileChangeMonitor != null) { _fileChangeMonitor.Dispose(); } if (_entryChangeMonitor != null) { _entryChangeMonitor.Dispose(); } #endif } // allow the first user to declare ownership internal bool Use() { return _bits.ChangeValue(USED, true); } // // Has a dependency changed? // public bool HasChanged { get {return _bits[CHANGED];} } public DateTime UtcLastModified { get { return _utcLastModified; } } protected void SetUtcLastModified(DateTime utcLastModified) { _utcLastModified = utcLastModified; } // // Add/remove an NotifyDependencyChanged notification. // internal void SetCacheDependencyChanged(ICacheDependencyChanged objNotify) { Debug.Assert(_objNotify == null, "_objNotify == null"); // Set this bit just in case our derived ctor forgot to call FinishInit() _bits[DERIVED_INIT] = true; if (!_bits[BASE_DISPOSED]) { _objNotify = objNotify; } } internal void AppendFileUniqueId(DepFileInfo depFileInfo, StringBuilder sb) { FileAttributesData fad = depFileInfo._fad; if (fad == null) { fad = FileAttributesData.NonExistantAttributesData; } sb.Append(depFileInfo._filename); sb.Append(fad.UtcLastWriteTime.Ticks.ToString("d", NumberFormatInfo.InvariantInfo)); sb.Append(fad.FileSize.ToString(CultureInfo.InvariantCulture)); } void InitUniqueID() { StringBuilder sb = null; object l_depFileInfos, l_entries; #if !FEATURE_PAL // no File Change Monitoring // get unique id from files l_depFileInfos = _depFileInfos; if (l_depFileInfos != null) { DepFileInfo oneDepFileInfo = l_depFileInfos as DepFileInfo; if (oneDepFileInfo != null) { sb = new StringBuilder(); AppendFileUniqueId(oneDepFileInfo, sb); } else { DepFileInfo[] depFileInfos = (DepFileInfo[]) l_depFileInfos; foreach (DepFileInfo depFileInfo in depFileInfos) { // ensure that we handle partially contructed // objects by checking filename for null if (depFileInfo._filename != null) { if (sb == null) sb = new StringBuilder(); AppendFileUniqueId(depFileInfo, sb); } } } } #endif // !FEATURE_PAL // get unique id from cache entries l_entries = _entries; if (l_entries != null) { CacheEntry oneEntry = l_entries as CacheEntry; if (oneEntry != null) { if (sb == null) sb = new StringBuilder(); sb.Append(oneEntry.Key); sb.Append(oneEntry.UtcCreated.Ticks.ToString(CultureInfo.InvariantCulture)); } else { CacheEntry[] entries = (CacheEntry[]) l_entries; foreach (CacheEntry entry in entries) { // ensure that we handle partially contructed // objects by checking entry for null if (entry != null) { if (sb == null) sb = new StringBuilder(); sb.Append(entry.Key); sb.Append(entry.UtcCreated.Ticks.ToString(CultureInfo.InvariantCulture)); } } } } if (sb != null) _uniqueID = sb.ToString(); #if DBG _isUniqueIDInitialized = true; #endif } public virtual string GetUniqueID() { #if DBG Debug.Assert(_isUniqueIDInitialized == true, "_isUniqueIDInitialized == true"); #endif return _uniqueID; } // // Return the cacheEntries monitored by this dependency // internal CacheEntry[] CacheEntries { get { if (_entries == null) { return null; } CacheEntry oneEntry = _entries as CacheEntry; if (oneEntry != null) { return new CacheEntry[1] {oneEntry}; } return (CacheEntry[]) _entries; } } // // This object has changed, so fire the NotifyDependencyChanged event. // We only allow this event to be fired once. // protected void NotifyDependencyChanged(Object sender, EventArgs e) { if (_bits.ChangeValue(CHANGED, true)) { _utcLastModified = DateTime.UtcNow; ICacheDependencyChanged objNotify = _objNotify; if (objNotify != null && !_bits[BASE_DISPOSED]) { Debug.Trace("CacheDependencyNotifyDependencyChanged", "change occurred"); objNotify.DependencyChanged(sender, e); } DisposeInternal(); } } // // ItemRemoved is called when a cache entry we are monitoring has been removed. // internal void ItemRemoved() { NotifyDependencyChanged(this, EventArgs.Empty); } // // FileChange is called when a file we are monitoring has changed. // void FileChange(Object sender, FileChangeEvent e) { Debug.Trace("CacheDependencyFileChange", "FileChange file=" + e.FileName + ";Action=" + e.Action); NotifyDependencyChanged(sender, e); } // // This will examine the dependency and determine if it's ONLY a file dependency or not // internal virtual bool IsFileDependency() { #if USE_MEMORY_CACHE if (CacheInternal.UseMemoryCache) { if (_entryChangeMonitor != null) { return false; } if (_fileChangeMonitor != null) { return true; } return false; } #endif object depInfos, l_entries; // Check and see if we are dependent on any cache entries l_entries = _entries; if (l_entries != null) { CacheEntry oneEntry = l_entries as CacheEntry; if (oneEntry != null) { return false; } else { CacheEntry[] entries = (CacheEntry[]) l_entries; if (entries != null && entries.Length > 0) { return false; } } } depInfos = _depFileInfos; if (depInfos != null) { DepFileInfo oneDepFileInfo = depInfos as DepFileInfo; if (oneDepFileInfo != null) { return true; } else { DepFileInfo[] depFileInfos = (DepFileInfo[]) depInfos; if (depFileInfos != null && depFileInfos.Length > 0) { return true; } } } return false; } // // This method will return only the file dependencies from this dependency // internal virtual string[] GetFileDependencies() { #if USE_MEMORY_CACHE if (CacheInternal.UseMemoryCache) { if (_fileChangeMonitor != null) { ReadOnlyCollection paths = _fileChangeMonitor.FilePaths; if (paths != null && paths.Count > 0) { string[] aryPaths = new string[paths.Count]; for (int i = 0; i < aryPaths.Length; i++) { aryPaths[i] = paths[i]; } return aryPaths; } } return null; } #endif object depInfos = _depFileInfos; if (depInfos != null) { DepFileInfo oneDepFileInfo = depInfos as DepFileInfo; if (oneDepFileInfo != null) { return new string[] {oneDepFileInfo._filename}; } else { DepFileInfo[] depFileInfos = (DepFileInfo[]) depInfos; string[] names = new string[depFileInfos.Length]; for (int i = 0; i < depFileInfos.Length; i++) { names[i] = depFileInfos[i]._filename; } return names; } } return null; } } public sealed class AggregateCacheDependency : CacheDependency, ICacheDependencyChanged { ArrayList _dependencies; bool _disposed; public AggregateCacheDependency() { // The ctor of every class derived from CacheDependency must call this. FinishInit(); } public void Add(params CacheDependency [] dependencies) { DateTime utcLastModified = DateTime.MinValue; if (dependencies == null) { throw new ArgumentNullException("dependencies"); } // copy array argument contents so they can't be changed beneath us dependencies = (CacheDependency []) dependencies.Clone(); // validate contents foreach (CacheDependency d in dependencies) { if (d == null) { throw new ArgumentNullException("dependencies"); } if (!d.Use()) { throw new InvalidOperationException(SR.GetString(SR.Cache_dependency_used_more_that_once)); } } // add dependencies, and check if any have changed bool hasChanged = false; lock (this) { if (!_disposed) { if (_dependencies == null) { _dependencies = new ArrayList(); } _dependencies.AddRange(dependencies); foreach (CacheDependency d in dependencies) { d.SetCacheDependencyChanged(this); if (d.UtcLastModified > utcLastModified) { utcLastModified = d.UtcLastModified; } if (d.HasChanged) { hasChanged = true; break; } } } } SetUtcLastModified(utcLastModified); // if a dependency has changed, notify others that we have changed. if (hasChanged) { NotifyDependencyChanged(this, EventArgs.Empty); } } // Dispose our dependencies. Note that the call to this // function is thread safe. protected override void DependencyDispose() { CacheDependency[] dependencies = null; lock (this) { _disposed = true; if (_dependencies != null) { dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency)); _dependencies = null; } } if (dependencies != null) { foreach (CacheDependency d in dependencies) { d.DisposeInternal(); } } } // Forward call from the aggregate to the CacheEntry /// void ICacheDependencyChanged.DependencyChanged(Object sender, EventArgs e) { NotifyDependencyChanged(sender, e); } public override string GetUniqueID() { StringBuilder sb = null; CacheDependency[] dependencies = null; //VSWhidbey 354570: return null if this AggregateCacheDependency cannot otherwise return a unique ID if (_dependencies == null) { return null; } lock (this) { if (_dependencies != null) { dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency)); } } if (dependencies != null) { foreach (CacheDependency dependency in dependencies) { string id = dependency.GetUniqueID(); if (id == null) { // When AggregateCacheDependency contains a dependency for which GetUniqueID() returns null, // it should return null itself. This is because it can no longer generate a UniqueID that // is guaranteed to be different when any of the dependencies change. return null; } if (sb == null) { sb = new StringBuilder(); } sb.Append(id); } } return sb != null ? sb.ToString() : null; } internal CacheDependency[] GetDependencyArray() { CacheDependency[] dependencies = null; lock (this) { if (_dependencies != null) { dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency)); } } return dependencies; } // // This will examine the dependencies and only return true if ALL dependencies are file dependencies // internal override bool IsFileDependency() { CacheDependency[] dependencies = null; dependencies = GetDependencyArray(); if (dependencies == null) { return false; } foreach (CacheDependency d in dependencies) { // We should only check if the type is either CacheDependency or the Aggregate. // Anything else, we can't guarantee that it's a file only dependency. if ( ! object.ReferenceEquals(d.GetType(), typeof(CacheDependency)) && ! object.ReferenceEquals(d.GetType(), typeof(AggregateCacheDependency)) ) { return false; } if (! d.IsFileDependency()) { return false; } } return true; } // // This method will return only the file dependencies from this dependency // internal override string[] GetFileDependencies() { ArrayList fileNames = null; CacheDependency[] dependencies = null; dependencies = GetDependencyArray(); if (dependencies == null) { return null; } foreach (CacheDependency d in dependencies) { // Check if the type is either CacheDependency or an Aggregate; // for anything else, we can't guarantee it's a file only dependency. if (object.ReferenceEquals(d.GetType(), typeof(CacheDependency)) || object.ReferenceEquals(d.GetType(), typeof(AggregateCacheDependency))) { string[] tmpFileNames = d.GetFileDependencies(); if (tmpFileNames != null) { if (fileNames == null) { fileNames = new ArrayList(); } fileNames.AddRange(tmpFileNames); } } } if (fileNames != null) { return (string[])fileNames.ToArray(typeof(string)); } else { return null; } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DesignBindingPicker.cs
- PolyBezierSegmentFigureLogic.cs
- DispatcherProcessingDisabled.cs
- HelpEvent.cs
- IsolatedStorageFilePermission.cs
- Int32CAMarshaler.cs
- InternalPolicyElement.cs
- EasingKeyFrames.cs
- NeutralResourcesLanguageAttribute.cs
- Profiler.cs
- HtmlEmptyTagControlBuilder.cs
- BasicDesignerLoader.cs
- Misc.cs
- MemoryMappedView.cs
- DetailsViewUpdateEventArgs.cs
- Nodes.cs
- PermissionSetTriple.cs
- CheckBoxField.cs
- TransformGroup.cs
- RoutedEventConverter.cs
- CapabilitiesUse.cs
- ApplicationContext.cs
- ApplicationServiceHelper.cs
- UnaryOperationBinder.cs
- ServiceConfigurationTraceRecord.cs
- Stream.cs
- SrgsOneOf.cs
- ImageBrush.cs
- ProfileGroupSettings.cs
- SmiConnection.cs
- XmlSchemaImport.cs
- TransportContext.cs
- MetadataPropertyvalue.cs
- HttpContext.cs
- KeyNotFoundException.cs
- TypedDataSetSchemaImporterExtensionFx35.cs
- XmlStringTable.cs
- _ProxyChain.cs
- RealProxy.cs
- Base64Decoder.cs
- ExpressionSelection.cs
- SystemIPGlobalStatistics.cs
- CodeTypeMember.cs
- QuerySelectOp.cs
- XmlException.cs
- IntegerCollectionEditor.cs
- NavigationFailedEventArgs.cs
- QueryComponents.cs
- invalidudtexception.cs
- FolderBrowserDialogDesigner.cs
- CaseStatementSlot.cs
- ClientBuildManagerCallback.cs
- ProviderCommandInfoUtils.cs
- AnimationLayer.cs
- Literal.cs
- ControlCachePolicy.cs
- DataContractSerializerElement.cs
- DataColumnMappingCollection.cs
- SqlParameterCollection.cs
- WebPartDescription.cs
- FixedSOMPage.cs
- ObjectCloneHelper.cs
- TableColumn.cs
- UIElement3D.cs
- DataGridTextBox.cs
- Vector3DCollection.cs
- SqlRowUpdatingEvent.cs
- storepermission.cs
- Msec.cs
- DataMisalignedException.cs
- GeneralTransform.cs
- MissingFieldException.cs
- ClientBuildManagerCallback.cs
- XmlSchemaDocumentation.cs
- ScriptingProfileServiceSection.cs
- StrokeNodeOperations2.cs
- X509WindowsSecurityToken.cs
- TemplateBuilder.cs
- SynchronizationLockException.cs
- XmlEventCache.cs
- ManagementInstaller.cs
- brushes.cs
- GPPOINT.cs
- ValueQuery.cs
- GlyphInfoList.cs
- MethodSignatureGenerator.cs
- XamlStream.cs
- DataServiceRequestException.cs
- BulletedListEventArgs.cs
- DecoderFallback.cs
- GAC.cs
- TableItemProviderWrapper.cs
- ReferencedAssembly.cs
- DynamicMethod.cs
- ProvidePropertyAttribute.cs
- MediaCommands.cs
- DeflateStream.cs
- CompositeCollection.cs
- ConfigXmlElement.cs
- DbConnectionPoolCounters.cs