|
//------------------------------------------------------------------------------
// <copyright file="CacheDependency.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
* 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
/// <devdoc>
/// <para>The <see langword='CacheDependency'/> 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
/// <see langword='Cache'/>.</para>
/// </devdoc>
// 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
Action<Object, EventArgs> _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 DepCacheInfo[] 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;
}
internal class DepCacheInfo {
internal CacheStoreProvider _cacheStore;
internal string _key;
}
static CacheDependency() {
s_stringsEmpty = new string[0];
s_entriesEmpty = new DepCacheInfo[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);
}
/// <devdoc>
/// <para>Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance
/// monitors a file or directory for changes.</para>
/// </devdoc>
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);
}
/// <devdoc>
/// <para>Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance monitors an array
/// files or directories for changes.</para>
/// </devdoc>
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);
}
/// <devdoc>
/// <para>Initializes a new instance of the System.Web.Cache.CacheDependency class. The new instance monitors an
/// array files, directories, and cache keys for changes.</para>
/// </devdoc>
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.InternalCache as MemCache;
_bits = new SafeBitVector32(0);
_utcLastModified = DateTime.MinValue;
IList<String> files = filenamesArg;
IList<String> keys = cachekeysArg;
if (dependency != null) {
ReadOnlyCollection<string> filePaths = (dependency._fileChangeMonitor != null) ? dependency._fileChangeMonitor.FilePaths : null;
ReadOnlyCollection<string> 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<String>(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<String>(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;
DepCacheInfo[] depEntries = s_entriesEmpty;
string [] filenames, cachekeys;
CacheStoreProvider cache;
_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 DepCacheInfo) {
depEntries = new DepCacheInfo[1] { (DepCacheInfo)(d_entries) };
}
else {
depEntries = (DepCacheInfo[])(d_entries);
// verify that the object was fully constructed
foreach (DepCacheInfo 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;
DateTime lastUpdated;
if (lenMyEntries > 0 && !_bits[CHANGED]) {
DepCacheInfo[] myEntries = new DepCacheInfo[lenMyEntries];
// Monitor entries from the existing cache dependency
int i = 0;
foreach (DepCacheInfo entry in depEntries) {
entry._cacheStore.AddDependent(entry._key, this, out lastUpdated);
myEntries[i++] = entry;
}
// Monitor new entries specified for this depenedency
// Entries must be added to cache, and created before the startTime
cache = isPublic ? HttpRuntime.Cache.ObjectCache : HttpRuntime.Cache.InternalCache;
foreach (string k in cachekeys) {
if (cache.AddDependent(k, this, out lastUpdated)) {
myEntries[i++] = new DepCacheInfo() { _cacheStore = cache, _key = k };
if (lastUpdated > _utcLastModified) {
_utcLastModified = lastUpdated;
}
if (lastUpdated > utcStart) { // Cache item has been updated since start, consider changed
#if DBG
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 (TakeOwnership()) {
// 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) {
DepCacheInfo oneEntry = l_entries as DepCacheInfo;
if (oneEntry != null) {
oneEntry._cacheStore.RemoveDependent(oneEntry._key, this);
}
else {
DepCacheInfo[] entries = (DepCacheInfo[])l_entries;
foreach (DepCacheInfo entry in entries) {
// ensure that we handle partially contructed
// objects by checking entry for null
if (entry != null) {
entry._cacheStore.RemoveDependent(entry._key, this);
}
}
}
}
#if USE_MEMORY_CACHE
if (_fileChangeMonitor != null) {
_fileChangeMonitor.Dispose();
}
if (_entryChangeMonitor != null) {
_entryChangeMonitor.Dispose();
}
#endif
}
/// <devdoc>
/// <para>Allow the first user to declare exclusive ownership of this dependency.</para>
/// </devdoc>
public bool TakeOwnership() {
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;
}
public void KeepDependenciesAlive() {
object l_entries = _entries;
if (l_entries != null) {
// update the last access time of every cache item that depends on this dependency
DepCacheInfo oneEntry = l_entries as DepCacheInfo;
if (oneEntry != null) {
oneEntry._cacheStore.Get(oneEntry._key);
return;
}
foreach (DepCacheInfo entry in (DepCacheInfo[])l_entries) {
if (entry != null) {
object item = entry._cacheStore.Get(entry._key);
}
}
}
}
/// <devdoc>
/// <para>Add an Action to handle notifying interested party in changes to this dependency.</para>
/// </devdoc>
public void SetCacheDependencyChanged(Action<Object, EventArgs> dependencyChangedAction) {
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 = dependencyChangedAction;
}
}
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) {
DepCacheInfo oneEntry = l_entries as DepCacheInfo;
if (oneEntry != null) {
if (sb == null)
sb = new StringBuilder();
sb.Append(oneEntry._key);
sb.Append(oneEntry.GetHashCode().ToString(CultureInfo.InvariantCulture));
}
else {
DepCacheInfo[] entries = (DepCacheInfo[])l_entries;
foreach (DepCacheInfo 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.GetHashCode().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;
}
//
// 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;
Action<Object, EventArgs> action = _objNotify as Action<Object, EventArgs>;
if (action != null && !_bits[BASE_DISPOSED]) {
Debug.Trace("CacheDependencyNotifyDependencyChanged", "change occurred");
action(sender, e);
}
DisposeInternal();
}
}
/// <devdoc>
/// <para>ItemRemoved should be called when an ICacheEntry we are monitoring has been removed.</para>
/// </devdoc>
public 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) {
DepCacheInfo oneEntry = l_entries as DepCacheInfo;
if (oneEntry != null) {
return false;
}
else {
DepCacheInfo[] entries = (DepCacheInfo[]) 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;
}
/// <summary>
/// This method will return only the file dependencies from this dependency
/// </summary>
/// <returns></returns>
public virtual string[] GetFileDependencies()
{
#if USE_MEMORY_CACHE
if (CacheInternal.UseMemoryCache) {
if (_fileChangeMonitor != null) {
ReadOnlyCollection<string> 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 {
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.TakeOwnership()) {
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((Object sender, EventArgs args) => {
DependencyChanged(sender, args);
});
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
/// <internalonly/>
void 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;
}
/// <summary>
/// This method will return only the file dependencies from this dependency
/// </summary>
/// <returns></returns>
public 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;
}
}
}
}
|