1 //------------------------------------------------------------------------------
2 // <copyright file="CacheDependency.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
10 * Copyright (c) 1998-1999, Microsoft Corporation
15 namespace System.Web.Caching {
16 using System.Collections;
19 using System.Threading;
20 using System.Web.Util;
21 using System.Security.Permissions;
22 using System.Globalization;
24 using System.Collections.Generic;
25 using System.Collections.ObjectModel;
26 using System.Runtime.Caching;
29 internal interface ICacheDependencyChanged {
30 void DependencyChanged(Object sender, EventArgs e);
35 /// <para>The <see langword='CacheDependency'/> class tracks cache dependencies, which can be files,
36 /// directories, or keys to other objects in the System.Web.Cache.Cache. When an object of this class
37 /// is constructed, it immediately begins monitoring objects on which it is
38 /// dependent for changes. This avoids losing the changes made between the time the
39 /// object to cache is created and the time it is inserted into the
40 /// <see langword='Cache'/>.</para>
43 // Overhead is 24 bytes + object header
44 public class CacheDependency : IDisposable {
47 bool _isUniqueIDInitialized;
50 string _uniqueID; // used by HttpCachePolicy for the ETag
51 object _depFileInfos; // files to monitor for changes, either a DepFileInfo or array of DepFileInfos
52 object _entries; // cache entries we are dependent on, either a string or array of strings
53 ICacheDependencyChanged _objNotify; // Associated object to notify when a change occurs
54 SafeBitVector32 _bits; // status bits for ready, used, changed, disposed
55 DateTime _utcLastModified; // Time of last modified item
57 HostFileChangeMonitor _fileChangeMonitor;
58 CacheEntryChangeMonitor _entryChangeMonitor;
61 static readonly string[] s_stringsEmpty;
62 static readonly CacheEntry[] s_entriesEmpty;
63 static readonly CacheDependency s_dependencyEmpty;
64 static readonly DepFileInfo[] s_depFileInfosEmpty;
66 static readonly TimeSpan FUTURE_FILETIME_BUFFER = new TimeSpan(0, 1, 0); // See VSWhidbey 400917
68 const int BASE_INIT = 0x01;
69 const int USED = 0x02;
70 const int CHANGED = 0x04;
71 const int BASE_DISPOSED = 0x08;
72 const int WANTS_DISPOSE = 0x10;
73 const int DERIVED_INIT = 0x20;
74 const int DERIVED_DISPOSED = 0x40;
76 internal class DepFileInfo {
77 internal string _filename;
78 internal FileAttributesData _fad;
81 static CacheDependency() {
82 s_stringsEmpty = new string[0];
83 s_entriesEmpty = new CacheEntry[0];
84 s_dependencyEmpty = new CacheDependency(0);
85 s_depFileInfosEmpty = new DepFileInfo[0];
88 // creates an empty dependency which is used only by s_dependencyEmpty
89 private CacheDependency(int bogus) {
90 Debug.Assert(s_dependencyEmpty == null, "s_dependencyEmpty == null");
95 protected CacheDependency() {
96 Init(true, null, null, null, DateTime.MaxValue);
101 /// <para>Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance
102 /// monitors a file or directory for changes.</para>
104 public CacheDependency(string filename) :
105 this (filename, DateTime.MaxValue) {
109 public CacheDependency(string filename, DateTime start) {
110 if (filename == null) {
114 DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start);
115 string[] filenames = new string[1] {filename};
116 Init(true, filenames, null, null, utcStart);
122 /// <para>Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance monitors an array
123 /// files or directories for changes.</para>
125 public CacheDependency(string[] filenames) {
126 Init(true, filenames, null, null, DateTime.MaxValue);
130 public CacheDependency(string[] filenames, DateTime start) {
131 DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start);
132 Init(true, filenames, null, null, utcStart);
137 /// <para>Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance monitors an
138 /// array files, directories, and cache keys for changes.</para>
140 public CacheDependency(string[] filenames, string[] cachekeys) {
141 Init(true, filenames, cachekeys, null, DateTime.MaxValue);
145 public CacheDependency(string[] filenames, string[] cachekeys, DateTime start) {
146 DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start);
147 Init(true, filenames, cachekeys, null, utcStart);
151 public CacheDependency(string[] filenames, string[] cachekeys, CacheDependency dependency) {
152 Init(true, filenames, cachekeys, dependency, DateTime.MaxValue);
156 public CacheDependency(string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime start) {
157 DateTime utcStart = DateTimeUtil.ConvertToUniversalTime(start);
158 Init(true, filenames, cachekeys, dependency, utcStart);
161 internal CacheDependency(int dummy, string filename) :
162 this(dummy, filename, DateTime.MaxValue) {
165 internal CacheDependency(int dummy, string filename, DateTime utcStart) {
166 if (filename == null) {
170 string[] filenames = new string[1] {filename};
171 Init(false, filenames, null, null, utcStart);
175 internal CacheDependency(int dummy, string[] filenames) {
176 Init(false, filenames, null, null, DateTime.MaxValue);
179 internal CacheDependency(int dummy, string[] filenames, DateTime utcStart) {
180 Init(false, filenames, null, null, utcStart);
183 internal CacheDependency(int dummy, string[] filenames, string[] cachekeys) {
184 Init(false, filenames, cachekeys, null, DateTime.MaxValue);
187 internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, DateTime utcStart) {
188 Init(false, filenames, cachekeys, null, utcStart);
191 internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, CacheDependency dependency) {
192 Init(false, filenames, cachekeys, dependency, DateTime.MaxValue);
195 internal CacheDependency(int dummy, string[] filenames, string[] cachekeys, CacheDependency dependency, DateTime utcStart) {
196 Init(false, filenames, cachekeys, dependency, utcStart);
200 void OnChangedCallback(object state) {
201 Debug.Trace("CacheDependencyFileChange", "OnChangedCallback fired");
202 NotifyDependencyChanged(this, EventArgs.Empty);
205 void InitForMemoryCache(bool isPublic, string[] filenamesArg, string[] cachekeysArg, CacheDependency dependency, DateTime utcStart) {
208 MemCache memCache = HttpRuntime.CacheInternal as MemCache;
209 _bits = new SafeBitVector32(0);
210 _utcLastModified = DateTime.MinValue;
211 IList<String> files = filenamesArg;
212 IList<String> keys = cachekeysArg;
213 if (dependency != null) {
214 ReadOnlyCollection<string> filePaths = (dependency._fileChangeMonitor != null) ? dependency._fileChangeMonitor.FilePaths : null;
215 ReadOnlyCollection<string> cacheKeys = (dependency._entryChangeMonitor != null) ? dependency._entryChangeMonitor.CacheKeys : null;
216 if (filePaths != null || filenamesArg != null) {
217 if (filePaths == null) {
218 files = filenamesArg;
220 else if (filenamesArg == null) {
224 files = new List<String>(filenamesArg.Length + filePaths.Count);
225 foreach (string f in filenamesArg) {
228 foreach (string f in filePaths) {
233 if (cacheKeys != null || cachekeysArg != null) {
234 if (cacheKeys == null) {
237 else if (cachekeysArg == null) {
241 keys = new List<String>(cachekeysArg.Length + cacheKeys.Count);
242 foreach (string f in cachekeysArg) {
245 foreach (string f in cacheKeys) {
252 _fileChangeMonitor = (files != null) ? new HostFileChangeMonitor(files) : null;
253 _entryChangeMonitor = (keys != null) ? memCache.CreateCacheEntryChangeMonitor(keys, isPublic) : null;
255 string uniqueId = null;
257 if (_fileChangeMonitor != null) {
258 _utcLastModified = _fileChangeMonitor.LastModified.UtcDateTime;
259 uniqueId = _fileChangeMonitor.UniqueId;
260 _fileChangeMonitor.NotifyOnChanged(new OnChangedCallback(OnChangedCallback));
262 if (_entryChangeMonitor != null) {
263 DateTime utcLastModified = _entryChangeMonitor.LastModified.UtcDateTime;
264 if (utcLastModified > _utcLastModified) {
265 _utcLastModified = utcLastModified;
267 uniqueId += _entryChangeMonitor.UniqueId;
268 _entryChangeMonitor.NotifyOnChanged(new OnChangedCallback(OnChangedCallback));
271 _uniqueID = uniqueId;
273 _isUniqueIDInitialized = true;
275 // check if file has changed since the start time
276 if (utcStart < DateTime.MaxValue) {
277 if (_utcLastModified > utcStart
278 && !(_utcLastModified - DateTime.UtcNow > FUTURE_FILETIME_BUFFER)) { // See VSWhidbey 400917
279 _bits[CHANGED] = true;
283 _bits[BASE_INIT] = true;
284 if (dependency != null && dependency._bits[CHANGED]) {
285 _bits[CHANGED] = true;
287 if (_bits[WANTS_DISPOSE] || _bits[CHANGED]) {
288 Debug.Trace("CacheDependencyInit", "WANTS_DISPOSE or CHANGED. InitForMemoryCache calling DisposeInternal");
295 _bits[BASE_INIT] = true;
296 Debug.Trace("CacheDependencyInit", "\n\nERROR in CacheDependency.InitForMemoryCache, calling DisposeInternal");
303 void Init(bool isPublic, string[] filenamesArg, string[] cachekeysArg, CacheDependency dependency, DateTime utcStart) {
305 if (CacheInternal.UseMemoryCache) {
306 InitForMemoryCache(isPublic, filenamesArg, cachekeysArg, dependency, utcStart);
310 DepFileInfo[] depFileInfos = s_depFileInfosEmpty;
311 CacheEntry[] depEntries = s_entriesEmpty;
312 string [] filenames, cachekeys;
313 CacheInternal cacheInternal;
315 _bits = new SafeBitVector32(0);
317 // copy array argument contents so they can't be changed beneath us
318 if (filenamesArg != null) {
319 filenames = (string []) filenamesArg.Clone();
325 if (cachekeysArg != null) {
326 cachekeys = (string []) cachekeysArg.Clone();
332 _utcLastModified = DateTime.MinValue;
335 // validate filenames array
336 if (filenames == null) {
337 filenames = s_stringsEmpty;
340 foreach (string f in filenames) {
342 throw new ArgumentNullException("filenamesArg");
345 // demand PathDiscovery if public
347 InternalSecurityPermissions.PathDiscovery(f).Demand();
352 if (cachekeys == null) {
353 cachekeys = s_stringsEmpty;
356 // validate cachekeys array
357 foreach (string k in cachekeys) {
359 throw new ArgumentNullException("cachekeysArg");
364 // copy all parts of another dependency if provided
365 if (dependency == null) {
366 dependency = s_dependencyEmpty;
369 if (dependency.GetType() != s_dependencyEmpty.GetType()) {
370 throw new ArgumentException(SR.GetString(SR.Invalid_Dependency_Type));
373 // Copy the parts of the dependency we need before
374 // we reference them, as the dependency can change
376 object d_depFileInfos = dependency._depFileInfos;
377 object d_entries = dependency._entries;
378 DateTime d_lastModified = dependency._utcLastModified;
380 // if the dependency we're copying has changed, we're done
381 if (dependency._bits[CHANGED]) {
382 _bits[CHANGED] = true;
383 // There is nothing to dispose because we haven't started
384 // monitoring anything yet. But we call DisposeInternal in
385 // order to set the WANTS_DISPOSE bit.
391 if (d_depFileInfos != null) {
392 if (d_depFileInfos is DepFileInfo) {
393 depFileInfos = new DepFileInfo[1] {(DepFileInfo) d_depFileInfos};
396 depFileInfos = (DepFileInfo[]) (d_depFileInfos);
400 // verify that the object was fully constructed
401 // and that we have permission to discover the file
402 foreach (DepFileInfo depFileInfo in depFileInfos) {
403 string f = depFileInfo._filename;
406 _bits[CHANGED] = true;
407 // There is nothing to dispose because we haven't started
408 // monitoring anything yet. But we call DisposeInternal in
409 // order to set the WANTS_DISPOSE bit.
414 // demand PathDiscovery if public
416 InternalSecurityPermissions.PathDiscovery(f).Demand();
421 // copy cache entries
422 if (d_entries != null) {
423 if (d_entries is CacheEntry) {
424 depEntries = new CacheEntry[1] {(CacheEntry) (d_entries)};
427 depEntries = (CacheEntry[]) (d_entries);
428 // verify that the object was fully constructed
429 foreach (CacheEntry entry in depEntries) {
431 _bits[CHANGED] = true;
432 // There is nothing to dispose because we haven't started
433 // monitoring anything yet. But we call DisposeInternal in
434 // order to set the WANTS_DISPOSE bit.
442 _utcLastModified = d_lastModified;
445 // Monitor files for changes
446 int lenMyDepFileInfos = depFileInfos.Length + filenames.Length;
447 if (lenMyDepFileInfos > 0) {
448 DepFileInfo[] myDepFileInfos = new DepFileInfo[lenMyDepFileInfos];
449 FileChangeEventHandler handler = new FileChangeEventHandler(this.FileChange);
450 FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor;
453 for (i = 0; i < lenMyDepFileInfos; i++) {
454 myDepFileInfos[i] = new DepFileInfo();
457 // monitor files from the existing dependency
458 // note that we don't check for start times in the existing dependency
460 foreach (DepFileInfo depFileInfo in depFileInfos) {
461 string f = depFileInfo._filename;
462 fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad);
463 myDepFileInfos[i]._filename = f;
468 DateTime utcNow = DateTime.MinValue;
469 foreach (string f in filenames) {
470 DateTime utcLastWrite = fmon.StartMonitoringPath(f, handler, out myDepFileInfos[i]._fad);
471 myDepFileInfos[i]._filename = f;
474 if (utcLastWrite > _utcLastModified) {
475 _utcLastModified = utcLastWrite;
478 // check if file has changed since the start time
479 if (utcStart < DateTime.MaxValue) {
480 if (utcNow == DateTime.MinValue) {
481 utcNow = DateTime.UtcNow;
484 Debug.Trace("CacheDependencyInit", "file=" + f + "; utcStart=" + utcStart + "; utcLastWrite=" + utcLastWrite);
485 if (utcLastWrite >= utcStart &&
486 !(utcLastWrite - utcNow > FUTURE_FILETIME_BUFFER)) { // See VSWhidbey 400917
487 Debug.Trace("CacheDependencyInit", "changes occurred since start time for file " + f);
488 _bits[CHANGED] = true;
494 if (myDepFileInfos.Length == 1) {
495 _depFileInfos = myDepFileInfos[0];
498 _depFileInfos = myDepFileInfos;
502 // Monitor other cache entries for changes
503 int lenMyEntries = depEntries.Length + cachekeys.Length;
504 if (lenMyEntries > 0 && !_bits[CHANGED]) {
505 CacheEntry[] myEntries = new CacheEntry[lenMyEntries];
507 // Monitor entries from the existing cache dependency
509 foreach (CacheEntry entry in depEntries) {
510 entry.AddCacheDependencyNotify(this);
511 myEntries[i++] = entry;
514 // Monitor new entries specified for this depenedency
515 // Entries must be added to cache, and created before the startTime
516 cacheInternal = HttpRuntime.CacheInternal;
517 foreach (string k in cachekeys) {
518 CacheEntry entry = (CacheEntry) cacheInternal.DoGet(isPublic, k, CacheGetOptions.ReturnCacheEntry);
520 entry.AddCacheDependencyNotify(this);
521 myEntries[i++] = entry;
523 if (entry.UtcCreated > _utcLastModified) {
524 _utcLastModified = entry.UtcCreated;
527 if ( entry.State != CacheEntry.EntryState.AddedToCache ||
528 entry.UtcCreated > utcStart) {
531 if (entry.State != CacheEntry.EntryState.AddedToCache) {
532 Debug.Trace("CacheDependencyInit", "Entry is not in cache, considered changed:" + k);
535 Debug.Trace("CacheDependencyInit", "Changes occurred to entry since start time:" + k);
539 _bits[CHANGED] = true;
544 Debug.Trace("CacheDependencyInit", "Cache item not found to create dependency on:" + k);
545 _bits[CHANGED] = true;
550 if (myEntries.Length == 1) {
551 _entries = myEntries[0];
554 _entries = myEntries;
558 _bits[BASE_INIT] = true;
559 if (dependency._bits[CHANGED]) {
560 _bits[CHANGED] = true;
563 if (_bits[WANTS_DISPOSE] || _bits[CHANGED]) {
567 Debug.Assert(_objNotify == null, "_objNotify == null");
570 // derived constructor will not execute due to the throw,
571 // so we just force a dispose on ourselves
572 _bits[BASE_INIT] = true;
582 public void Dispose() {
583 // Set this bit just in case our derived ctor forgot to call FinishInit()
584 _bits[DERIVED_INIT] = true;
587 // Do the dispose only if the cache has not already used us
592 protected internal void FinishInit() {
593 _bits[DERIVED_INIT] = true;
595 if (_bits[WANTS_DISPOSE]) {
601 * Shutdown all dependency monitoring and firing of NotifyDependencyChanged notification.
603 internal void DisposeInternal() {
604 _bits[WANTS_DISPOSE] = true;
606 if (_bits[DERIVED_INIT]) {
607 if (_bits.ChangeValue(DERIVED_DISPOSED, true)) {
608 // Dispose derived classes
613 if (_bits[BASE_INIT]) {
614 if (_bits.ChangeValue(BASE_DISPOSED, true)) {
621 // Allow derived class to dispose itself
623 protected virtual void DependencyDispose() {
624 // We do our own dispose work in DisposeOurself, so that
625 // we don't rely on derived classes calling their base
626 // DependencyDispose for us to function correctly.
629 void DisposeOurself() {
630 // guarantee that we execute only once if an exception
631 // is thrown from this function by nulling fields before
633 object l_depFileInfos = _depFileInfos;
634 object l_entries = _entries;
637 _depFileInfos = null;
640 // stop monitoring files
641 if (l_depFileInfos != null) {
642 FileChangesMonitor fmon = HttpRuntime.FileChangesMonitor;
644 DepFileInfo oneDepFileInfo = l_depFileInfos as DepFileInfo;
645 if (oneDepFileInfo != null) {
646 fmon.StopMonitoringPath(oneDepFileInfo._filename, this);
649 DepFileInfo[] depFileInfos = (DepFileInfo[]) l_depFileInfos;
650 foreach (DepFileInfo depFileInfo in depFileInfos) {
651 // ensure that we handle partially contructed
652 // objects by checking filename for null
653 string filename = depFileInfo._filename;
654 if (filename != null) {
655 fmon.StopMonitoringPath(filename, this);
661 // stop monitoring cache items
662 if (l_entries != null) {
663 CacheEntry oneEntry = l_entries as CacheEntry;
664 if (oneEntry != null) {
665 oneEntry.RemoveCacheDependencyNotify(this);
668 CacheEntry[] entries = (CacheEntry[]) l_entries;
669 foreach (CacheEntry entry in entries) {
670 // ensure that we handle partially contructed
671 // objects by checking entry for null
673 entry.RemoveCacheDependencyNotify(this);
680 if (_fileChangeMonitor != null) {
681 _fileChangeMonitor.Dispose();
683 if (_entryChangeMonitor != null) {
684 _entryChangeMonitor.Dispose();
689 // allow the first user to declare ownership
690 internal bool Use() {
691 return _bits.ChangeValue(USED, true);
695 // Has a dependency changed?
698 public bool HasChanged {
699 get {return _bits[CHANGED];}
703 public DateTime UtcLastModified {
705 return _utcLastModified;
710 protected void SetUtcLastModified(DateTime utcLastModified) {
711 _utcLastModified = utcLastModified;
715 // Add/remove an NotifyDependencyChanged notification.
717 internal void SetCacheDependencyChanged(ICacheDependencyChanged objNotify) {
718 Debug.Assert(_objNotify == null, "_objNotify == null");
720 // Set this bit just in case our derived ctor forgot to call FinishInit()
721 _bits[DERIVED_INIT] = true;
723 if (!_bits[BASE_DISPOSED]) {
724 _objNotify = objNotify;
728 internal void AppendFileUniqueId(DepFileInfo depFileInfo, StringBuilder sb) {
729 FileAttributesData fad = depFileInfo._fad;
732 fad = FileAttributesData.NonExistantAttributesData;
735 sb.Append(depFileInfo._filename);
736 sb.Append(fad.UtcLastWriteTime.Ticks.ToString("d", NumberFormatInfo.InvariantInfo));
737 sb.Append(fad.FileSize.ToString(CultureInfo.InvariantCulture));
740 void InitUniqueID() {
741 StringBuilder sb = null;
742 object l_depFileInfos, l_entries;
744 #if !FEATURE_PAL // no File Change Monitoring
745 // get unique id from files
746 l_depFileInfos = _depFileInfos;
747 if (l_depFileInfos != null) {
748 DepFileInfo oneDepFileInfo = l_depFileInfos as DepFileInfo;
749 if (oneDepFileInfo != null) {
750 sb = new StringBuilder();
751 AppendFileUniqueId(oneDepFileInfo, sb);
754 DepFileInfo[] depFileInfos = (DepFileInfo[]) l_depFileInfos;
755 foreach (DepFileInfo depFileInfo in depFileInfos) {
756 // ensure that we handle partially contructed
757 // objects by checking filename for null
758 if (depFileInfo._filename != null) {
760 sb = new StringBuilder();
761 AppendFileUniqueId(depFileInfo, sb);
767 #endif // !FEATURE_PAL
768 // get unique id from cache entries
769 l_entries = _entries;
770 if (l_entries != null) {
771 CacheEntry oneEntry = l_entries as CacheEntry;
772 if (oneEntry != null) {
774 sb = new StringBuilder();
775 sb.Append(oneEntry.Key);
776 sb.Append(oneEntry.UtcCreated.Ticks.ToString(CultureInfo.InvariantCulture));
779 CacheEntry[] entries = (CacheEntry[]) l_entries;
780 foreach (CacheEntry entry in entries) {
781 // ensure that we handle partially contructed
782 // objects by checking entry for null
785 sb = new StringBuilder();
786 sb.Append(entry.Key);
787 sb.Append(entry.UtcCreated.Ticks.ToString(CultureInfo.InvariantCulture));
794 _uniqueID = sb.ToString();
797 _isUniqueIDInitialized = true;
801 public virtual string GetUniqueID() {
803 Debug.Assert(_isUniqueIDInitialized == true, "_isUniqueIDInitialized == true");
809 // Return the cacheEntries monitored by this dependency
811 internal CacheEntry[] CacheEntries {
813 if (_entries == null) {
817 CacheEntry oneEntry = _entries as CacheEntry;
818 if (oneEntry != null) {
819 return new CacheEntry[1] {oneEntry};
822 return (CacheEntry[]) _entries;
827 // This object has changed, so fire the NotifyDependencyChanged event.
828 // We only allow this event to be fired once.
831 protected void NotifyDependencyChanged(Object sender, EventArgs e) {
832 if (_bits.ChangeValue(CHANGED, true)) {
833 _utcLastModified = DateTime.UtcNow;
835 ICacheDependencyChanged objNotify = _objNotify;
836 if (objNotify != null && !_bits[BASE_DISPOSED]) {
837 Debug.Trace("CacheDependencyNotifyDependencyChanged", "change occurred");
838 objNotify.DependencyChanged(sender, e);
846 // ItemRemoved is called when a cache entry we are monitoring has been removed.
848 internal void ItemRemoved() {
849 NotifyDependencyChanged(this, EventArgs.Empty);
853 // FileChange is called when a file we are monitoring has changed.
855 void FileChange(Object sender, FileChangeEvent e) {
856 Debug.Trace("CacheDependencyFileChange", "FileChange file=" + e.FileName + ";Action=" + e.Action);
857 NotifyDependencyChanged(sender, e);
861 // This will examine the dependency and determine if it's ONLY a file dependency or not
863 internal virtual bool IsFileDependency()
866 if (CacheInternal.UseMemoryCache) {
867 if (_entryChangeMonitor != null) {
871 if (_fileChangeMonitor != null) {
878 object depInfos, l_entries;
880 // Check and see if we are dependent on any cache entries
881 l_entries = _entries;
882 if (l_entries != null) {
883 CacheEntry oneEntry = l_entries as CacheEntry;
884 if (oneEntry != null) {
888 CacheEntry[] entries = (CacheEntry[]) l_entries;
889 if (entries != null && entries.Length > 0) {
895 depInfos = _depFileInfos;
896 if (depInfos != null) {
897 DepFileInfo oneDepFileInfo = depInfos as DepFileInfo;
898 if (oneDepFileInfo != null) {
902 DepFileInfo[] depFileInfos = (DepFileInfo[]) depInfos;
903 if (depFileInfos != null && depFileInfos.Length > 0) {
913 /// This method will return only the file dependencies from this dependency
915 /// <returns></returns>
916 public virtual string[] GetFileDependencies()
919 if (CacheInternal.UseMemoryCache) {
920 if (_fileChangeMonitor != null) {
921 ReadOnlyCollection<string> paths = _fileChangeMonitor.FilePaths;
922 if (paths != null && paths.Count > 0) {
923 string[] aryPaths = new string[paths.Count];
924 for (int i = 0; i < aryPaths.Length; i++) {
925 aryPaths[i] = paths[i];
933 object depInfos = _depFileInfos;
935 if (depInfos != null) {
936 DepFileInfo oneDepFileInfo = depInfos as DepFileInfo;
937 if (oneDepFileInfo != null) {
938 return new string[] {oneDepFileInfo._filename};
941 DepFileInfo[] depFileInfos = (DepFileInfo[]) depInfos;
942 string[] names = new string[depFileInfos.Length];
943 for (int i = 0; i < depFileInfos.Length; i++) {
944 names[i] = depFileInfos[i]._filename;
955 public sealed class AggregateCacheDependency : CacheDependency, ICacheDependencyChanged {
956 ArrayList _dependencies;
960 public AggregateCacheDependency() {
961 // The ctor of every class derived from CacheDependency must call this.
966 public void Add(params CacheDependency [] dependencies) {
967 DateTime utcLastModified = DateTime.MinValue;
969 if (dependencies == null) {
970 throw new ArgumentNullException("dependencies");
973 // copy array argument contents so they can't be changed beneath us
974 dependencies = (CacheDependency []) dependencies.Clone();
977 foreach (CacheDependency d in dependencies) {
979 throw new ArgumentNullException("dependencies");
983 throw new InvalidOperationException(SR.GetString(SR.Cache_dependency_used_more_that_once));
987 // add dependencies, and check if any have changed
988 bool hasChanged = false;
991 if (_dependencies == null) {
992 _dependencies = new ArrayList();
995 _dependencies.AddRange(dependencies);
997 foreach (CacheDependency d in dependencies) {
998 d.SetCacheDependencyChanged(this);
1000 if (d.UtcLastModified > utcLastModified) {
1001 utcLastModified = d.UtcLastModified;
1012 SetUtcLastModified(utcLastModified);
1014 // if a dependency has changed, notify others that we have changed.
1016 NotifyDependencyChanged(this, EventArgs.Empty);
1020 // Dispose our dependencies. Note that the call to this
1021 // function is thread safe.
1023 protected override void DependencyDispose() {
1024 CacheDependency[] dependencies = null;
1028 if (_dependencies != null) {
1029 dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency));
1030 _dependencies = null;
1034 if (dependencies != null) {
1035 foreach (CacheDependency d in dependencies) {
1036 d.DisposeInternal();
1041 // Forward call from the aggregate to the CacheEntry
1044 void ICacheDependencyChanged.DependencyChanged(Object sender, EventArgs e) {
1045 NotifyDependencyChanged(sender, e);
1049 public override string GetUniqueID() {
1050 StringBuilder sb = null;
1051 CacheDependency[] dependencies = null;
1053 //VSWhidbey 354570: return null if this AggregateCacheDependency cannot otherwise return a unique ID
1054 if (_dependencies == null) {
1059 if (_dependencies != null) {
1060 dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency));
1064 if (dependencies != null) {
1065 foreach (CacheDependency dependency in dependencies) {
1066 string id = dependency.GetUniqueID();
1069 // When AggregateCacheDependency contains a dependency for which GetUniqueID() returns null,
1070 // it should return null itself. This is because it can no longer generate a UniqueID that
1071 // is guaranteed to be different when any of the dependencies change.
1076 sb = new StringBuilder();
1082 return sb != null ? sb.ToString() : null;
1085 internal CacheDependency[] GetDependencyArray()
1087 CacheDependency[] dependencies = null;
1090 if (_dependencies != null) {
1091 dependencies = (CacheDependency[]) _dependencies.ToArray(typeof(CacheDependency));
1095 return dependencies;
1099 // This will examine the dependencies and only return true if ALL dependencies are file dependencies
1101 internal override bool IsFileDependency()
1103 CacheDependency[] dependencies = null;
1105 dependencies = GetDependencyArray();
1106 if (dependencies == null) {
1110 foreach (CacheDependency d in dependencies) {
1111 // We should only check if the type is either CacheDependency or the Aggregate.
1112 // Anything else, we can't guarantee that it's a file only dependency.
1113 if ( ! object.ReferenceEquals(d.GetType(), typeof(CacheDependency)) &&
1114 ! object.ReferenceEquals(d.GetType(), typeof(AggregateCacheDependency)) ) {
1118 if (! d.IsFileDependency()) {
1127 /// This method will return only the file dependencies from this dependency
1129 /// <returns></returns>
1130 public override string[] GetFileDependencies()
1132 ArrayList fileNames = null;
1133 CacheDependency[] dependencies = null;
1135 dependencies = GetDependencyArray();
1136 if (dependencies == null) {
1140 foreach (CacheDependency d in dependencies) {
1141 // Check if the type is either CacheDependency or an Aggregate;
1142 // for anything else, we can't guarantee it's a file only dependency.
1143 if (object.ReferenceEquals(d.GetType(), typeof(CacheDependency))
1144 || object.ReferenceEquals(d.GetType(), typeof(AggregateCacheDependency))) {
1146 string[] tmpFileNames = d.GetFileDependencies();
1148 if (tmpFileNames != null) {
1150 if (fileNames == null) {
1151 fileNames = new ArrayList();
1154 fileNames.AddRange(tmpFileNames);
1159 if (fileNames != null) {
1160 return (string[])fileNames.ToArray(typeof(string));