├── .gitattributes ├── AltComparison.md ├── FileSystemWatcherAlts.sln ├── FileSystemWatcherAlts.sln.DotSettings.user ├── FileSystemWatcherAlts ├── FileSystemOverseer.cs ├── FileSystemWatcherAlts.csproj ├── IFileSystemWatcher.cs ├── Icon │ └── noun_160432_cc.png ├── Polling │ ├── FileSystemPoller.cs │ ├── IFileSystemPoller.cs │ └── PollingType.cs ├── Properties │ └── AssemblyInfo.cs ├── Utils │ ├── Extentions │ │ ├── CollectionsExtentions.cs │ │ ├── DirectoryExtentions.cs │ │ └── ExceptionsExtentions.cs │ ├── FileSystemFakeWatcher.cs │ ├── WatcherErrorHandlingPolicy.cs │ └── WatcherErrorHandlingType.cs ├── Wrappers │ ├── FileSystemAutoRefreshingWatcher.cs │ ├── FileSystemRefreshableWatcher.cs │ ├── FileSystemWatcherAdapter.cs │ └── FileSystemWatcherWrapper.cs └── packages.config ├── LICENSE ├── README.md └── packages └── Polly.2.2.3 ├── Polly.2.2.3.nupkg └── lib ├── net35 ├── Polly.dll └── Polly.xml ├── net40 ├── Polly.dll └── Polly.xml ├── net45 ├── Polly.dll └── Polly.xml └── portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1 ├── Polly.dll └── Polly.xml /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /AltComparison.md: -------------------------------------------------------------------------------- 1 | | | |FileSystemWatcher|FileSystemRefreshableWatcher|FileSystemAutoRefreshingWatcher|FileSystemPoller|FileSystemOverseer | 2 | |------------- | -------------| -------------|-------------| -------------|-------------| -------------| 3 | |**Supported Events**|**Created**|TRUE|TRUE|TRUE|TRUE|TRUE| 4 | | |**Deleted**|TRUE|TRUE|TRUE|TRUE|TRUE| 5 | | |**Changed**|TRUE|TRUE|TRUE|FALSE|TRUE\*| 6 | | |**Renamed**|TRUE|TRUE|TRUE|FALSE|TRUE\*| 7 | | |**Error**|TRUE|TRUE|TRUE|TRUE|TRUE| 8 | |**Issues Recovery**|**Remote Host Disconnected**|Breaks|User can refresh|Triggers watcher refresh|Continues when available|Triggers watcher refresh| 9 | | |**Remote Folder Deleted**|Breaks|User can refresh|Triggers watcher refresh|Continues when available|Triggers watcher refresh| 10 | | |**Internal Buffer Overflow**|Misses Files|Misses Files|Misses Files|No effect|Triggers polling| 11 | | |**Local Folder Deleted**|Breaks|User can refresh|User can refresh|Continues when available|Triggers watcher refresh| 12 | 13 | 14 | \* Supported while the internal buffer doesn't overflow. Falls back to Created/Deleted events instead of renames if it does. 15 | 16 | **A few rules of thumb:** 17 | 18 | * If you are not expecting heavy loads, consider using FileSystemAutoRefreshingWatcher. It provides the most reliability and lowest overhead for that case. 19 | * If you need "Renamed" or "Changed" update events the FileSystemPoller can't help you as it doesn't monitor them. 20 | * If you need reliability at all costs use FileSytemOverseer. Its overhead is higher than the other alternatives because it utilizes both a FileSystemAutoRefreshingWatcher and a FileSystemPoller 21 | but the combination of both gurantees the most immediate and precise reports in this library. 22 | -------------------------------------------------------------------------------- /FileSystemWatcherAlts.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileSystemWatcherAlts", "FileSystemWatcherAlts\FileSystemWatcherAlts.csproj", "{195F032D-DA21-40A0-ABFF-6FC4887A6839}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {195F032D-DA21-40A0-ABFF-6FC4887A6839}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {195F032D-DA21-40A0-ABFF-6FC4887A6839}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {195F032D-DA21-40A0-ABFF-6FC4887A6839}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {195F032D-DA21-40A0-ABFF-6FC4887A6839}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /FileSystemWatcherAlts.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 |  2 | <AssemblyExplorer> 3 | <Assembly Path="C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.Data.DataSetExtensions.dll" /> 4 | <Assembly Path="c:\users\shai\documents\visual studio 2015\Projects\FileSystemWatcherAlts\packages\Moq.4.2.1507.0118\lib\net40\Moq.dll" /> 5 | </AssemblyExplorer> -------------------------------------------------------------------------------- /FileSystemWatcherAlts/FileSystemOverseer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Runtime.InteropServices; 7 | using System.Threading.Tasks; 8 | using FileSystemWatcherAlts.Polling; 9 | using FileSystemWatcherAlts.Utils; 10 | using FileSystemWatcherAlts.Utils.Extentions; 11 | using FileSystemWatcherAlts.Wrappers; 12 | 13 | namespace FileSystemWatcherAlts 14 | { 15 | [DebuggerDisplay("Path = {Path}, Filter = {Filter}, EnableRaisingEvents = {_enableRaisingEvents}")] 16 | /// 17 | /// A FilleSystemWatcher wrapper which detects common FileSystemWatcher issues and resolves them. 18 | /// also performs periodic polling to increase reliability. 19 | /// 20 | public class FileSystemOverseer : FileSystemAutoRefreshingWatcher 21 | { 22 | #region Fields 23 | 24 | private readonly IFileSystemPoller _poller; 25 | 26 | private bool _enableRaisingEvents; 27 | 28 | private readonly object _reportedFilesLock; 29 | private readonly HashSet _reportedItems; 30 | 31 | #endregion 32 | 33 | #region Properties 34 | 35 | /// 36 | /// Defines a delay (in milliseconds) between processing poller reports. 37 | /// The main reason to delay such reports is to allow the more descriptive reports of the watcher to be processed. 38 | /// 39 | [Browsable(false)] 40 | public int PollerReportsDelay { get; set; } = 100; 41 | 42 | public override bool EnableRaisingEvents 43 | { 44 | get 45 | { 46 | return _enableRaisingEvents; 47 | } 48 | set 49 | { 50 | _enableRaisingEvents = value; 51 | InternalWatcher.EnableRaisingEvents = value; 52 | _poller.EnableRaisingEvents = value; 53 | } 54 | } 55 | public override string Filter 56 | { 57 | get 58 | { 59 | return InternalWatcher.Filter; 60 | } 61 | set 62 | { 63 | InternalWatcher.Filter = value; 64 | _poller.Filter = value; 65 | } 66 | } 67 | public override bool IncludeSubdirectories 68 | { 69 | get 70 | { 71 | return InternalWatcher.IncludeSubdirectories; 72 | } 73 | set 74 | { 75 | bool lastEREvalue = _enableRaisingEvents; 76 | _enableRaisingEvents = false; 77 | 78 | InternalWatcher.IncludeSubdirectories = value; 79 | _poller.IncludeSubdirectories = value; 80 | _poller.ForcePoll(); 81 | 82 | _enableRaisingEvents = lastEREvalue; 83 | 84 | } 85 | } 86 | public override int InternalBufferSize 87 | { 88 | get 89 | { 90 | return InternalWatcher.InternalBufferSize; 91 | } 92 | set 93 | { 94 | InternalWatcher.InternalBufferSize = value; 95 | 96 | } 97 | } 98 | public override NotifyFilters NotifyFilter 99 | { 100 | get 101 | { 102 | return InternalWatcher.NotifyFilter; 103 | } 104 | set 105 | { 106 | InternalWatcher.NotifyFilter = value; 107 | } 108 | } 109 | public override string Path 110 | { 111 | get 112 | { 113 | return InternalWatcher.Path; 114 | } 115 | set 116 | { 117 | bool lastEREvalue = _enableRaisingEvents; 118 | _enableRaisingEvents = false; 119 | 120 | InternalWatcher.Path = value; 121 | _poller.Path = value; 122 | 123 | _enableRaisingEvents = lastEREvalue; 124 | } 125 | } 126 | 127 | #endregion 128 | 129 | #region Constructors 130 | 131 | public FileSystemOverseer(IFileSystemPoller poller, IFileSystemWatcher watcher) : base(watcher) 132 | { 133 | _reportedItems = new HashSet(); 134 | _reportedFilesLock = new object(); 135 | 136 | InitPollerErrorPolicies(); 137 | 138 | // Initiating poller 139 | _poller = poller; 140 | if(_poller.Path != watcher.Path) 141 | { 142 | _poller.Path = watcher.Path; 143 | } 144 | 145 | EnableRaisingEvents = false; 146 | 147 | _poller.Created += OnCreatedPolled; 148 | _poller.Deleted += OnDeletedPolled; 149 | _poller.Error += OnPollerError; 150 | 151 | // Getting initial directory content by forcing a poll 152 | _poller.PollingType = PollingType.Poll; 153 | _poller.ForcePoll(); 154 | 155 | // For the rest of the Overseer's lifespan, keep the poller as a 'watcher' 156 | _poller.PollingType = PollingType.Watch; 157 | } 158 | 159 | public FileSystemOverseer(IFileSystemPoller poller, FileSystemWatcher watcher) : this(poller, new FileSystemWatcherAdapter(watcher)) 160 | { 161 | } 162 | 163 | public FileSystemOverseer(IFileSystemPoller poller) : this(poller, new FileSystemWatcherAdapter(poller.Path, poller.Filter)) 164 | { 165 | } 166 | 167 | public FileSystemOverseer(int pollingInterval) : this(new FileSystemPoller(pollingInterval), new FileSystemWatcher()) 168 | { 169 | } 170 | 171 | public FileSystemOverseer(int pollingInterval, string path) : this(new FileSystemPoller(pollingInterval, path), new FileSystemWatcher(path)) 172 | { 173 | } 174 | 175 | public FileSystemOverseer(int pollingInterval, string path, string filter) : this(new FileSystemPoller(pollingInterval, path, filter), new FileSystemWatcher(path, filter)) 176 | { 177 | } 178 | 179 | private void InitPollerErrorPolicies() 180 | { 181 | var dirNotFoundPolicy = new WatcherErrorHandlingPolicy(typeof(DirectoryNotFoundException), 182 | "When the poller indicates a 'directory not found' exception check if it's the main watched directory or sub-dir." + 183 | "If it's the main directory - refresh the watcher.", 184 | exception => (exception as DirectoryNotFoundException)?.Path() == Path 185 | ? WatcherErrorHandlingType.Refresh | WatcherErrorHandlingType.Swallow 186 | : WatcherErrorHandlingType.Forward); 187 | 188 | var unAuthPolicy = new WatcherErrorHandlingPolicy(typeof(UnauthorizedAccessException), 189 | "When the poller indicates an 'unauthorized access' exception check if it's access was denied to the main watched directory or file/sub-dir." + 190 | "If it's the main directory - refresh the watcher.", 191 | exception => (exception as UnauthorizedAccessException)?.Path() == Path 192 | ? WatcherErrorHandlingType.Refresh | WatcherErrorHandlingType.Swallow 193 | : WatcherErrorHandlingType.Forward); 194 | 195 | AddPolicy(dirNotFoundPolicy); 196 | AddPolicy(unAuthPolicy); 197 | } 198 | 199 | #endregion 200 | 201 | #region Event Handling Methods 202 | 203 | // Event handlers for the wrapped watcher and the poller (a delay) 204 | protected override void OnCreated(object sender, FileSystemEventArgs fileSystemEventArgs) 205 | { 206 | lock (_reportedFilesLock) 207 | { 208 | // If the files was already reported - return 209 | if (_reportedItems.Contains(fileSystemEventArgs.FullPath)) 210 | { 211 | return; 212 | } 213 | 214 | // Other wise: 215 | // 1. Add to reported files set 216 | _reportedItems.Add(fileSystemEventArgs.FullPath); 217 | } 218 | 219 | // 2. report to subscribers 220 | if (!_enableRaisingEvents) return; 221 | base.OnCreated(sender, fileSystemEventArgs); 222 | } 223 | 224 | protected override void OnDeleted(object sender, FileSystemEventArgs fileSystemEventArgs) 225 | { 226 | 227 | lock (_reportedFilesLock) 228 | { 229 | // If the files was already reported - return 230 | if (!_reportedItems.Contains(fileSystemEventArgs.FullPath)) 231 | { 232 | return; 233 | } 234 | 235 | 236 | // Other wise: 237 | // 1. Try to remove said file. If the removal fails - return 238 | if (!_reportedItems.Remove(fileSystemEventArgs.FullPath)) return; 239 | } 240 | 241 | // 2. report to subscribers 242 | if (!_enableRaisingEvents) return; 243 | base.OnDeleted(sender, fileSystemEventArgs); 244 | } 245 | 246 | protected override void OnChanged(object sender, FileSystemEventArgs fileSystemEventArgs) 247 | { 248 | if (!_enableRaisingEvents) return; 249 | base.OnChanged(sender,fileSystemEventArgs); 250 | } 251 | 252 | protected override void OnRenamed(object sender, RenamedEventArgs fileSystemEventArgs) 253 | { 254 | lock (_reportedFilesLock) 255 | { 256 | // If a file with the new name was already reported - return 257 | if (_reportedItems.Contains(fileSystemEventArgs.FullPath)) 258 | { 259 | return; 260 | } 261 | 262 | // 1. If the file's old name existed in the storage - remove it 263 | if (_reportedItems.Contains(fileSystemEventArgs.OldFullPath)) 264 | { 265 | _reportedItems.Remove(fileSystemEventArgs.OldFullPath); 266 | } 267 | 268 | // 2. Add new path to the reportedFiles list 269 | _reportedItems.Add(fileSystemEventArgs.FullPath); 270 | } 271 | 272 | // 3. report to subscribers 273 | if (!_enableRaisingEvents) return; 274 | base.OnRenamed(sender, fileSystemEventArgs); 275 | } 276 | 277 | protected override void OnError(object sender, ErrorEventArgs e) 278 | { 279 | var ex = e.GetException(); 280 | if (ex is InternalBufferOverflowException) 281 | { 282 | _poller.ForcePoll(); 283 | } 284 | 285 | base.OnError(sender, e); 286 | } 287 | 288 | // Events raised by the poller will invoke these methods first: 289 | private void OnCreatedPolled(object sender, FileSystemEventArgs fileSystemEventArgs) 290 | { 291 | Task.Delay(PollerReportsDelay).ContinueWith(task => OnCreated(sender, fileSystemEventArgs)); 292 | } 293 | 294 | private void OnDeletedPolled(object sender, FileSystemEventArgs fileSystemEventArgs) 295 | { 296 | 297 | Task.Delay(PollerReportsDelay).ContinueWith(task => OnDeleted(sender, fileSystemEventArgs)); 298 | } 299 | 300 | private void OnPollerError(object sender, ErrorEventArgs e) 301 | { 302 | base.OnError(sender, e); 303 | } 304 | 305 | #endregion 306 | 307 | #region IDisposeable Methods 308 | 309 | ~FileSystemOverseer() 310 | { 311 | Dispose(false); 312 | } 313 | 314 | public new void Dispose() 315 | { 316 | Dispose(true); 317 | GC.SuppressFinalize(this); 318 | } 319 | 320 | private void Dispose(bool disposing) 321 | { 322 | _poller.Created -= OnCreatedPolled; 323 | _poller.Deleted -= OnDeletedPolled; 324 | _poller.Error -= OnPollerError; 325 | _poller.Dispose(); 326 | 327 | if (disposing) 328 | { 329 | base.Dispose(); 330 | } 331 | } 332 | 333 | #endregion 334 | 335 | #region ICloneable Methods 336 | 337 | public override object Clone() 338 | { 339 | var clonedPoller = (IFileSystemPoller) _poller.Clone(); 340 | 341 | var clonedEncapsWatcher = (IFileSystemWatcher) InternalWatcher.Clone(); 342 | 343 | var clonedOverseer = new FileSystemOverseer(clonedPoller, clonedEncapsWatcher) 344 | { PollerReportsDelay = this.PollerReportsDelay }; 345 | 346 | clonedOverseer.ClearPolicies(); 347 | foreach (var policy in ErrorHandlingPolicies) 348 | { 349 | clonedOverseer.AddPolicy(policy); 350 | } 351 | 352 | return clonedOverseer; 353 | } 354 | 355 | #endregion 356 | } 357 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/FileSystemWatcherAlts.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {195F032D-DA21-40A0-ABFF-6FC4887A6839} 8 | Library 9 | Properties 10 | FileSystemWatcherAlts 11 | FileSystemWatcherAlts 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | true 26 | false 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | ..\packages\Polly.2.2.3\lib\net45\Polly.dll 40 | True 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 75 | -------------------------------------------------------------------------------- /FileSystemWatcherAlts/IFileSystemWatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace FileSystemWatcherAlts 5 | { 6 | /// 7 | /// Defines properties, events and methods for a FileSystemWatcher-like class 8 | /// 9 | public interface IFileSystemWatcher : IDisposable, ICloneable 10 | { 11 | bool EnableRaisingEvents { get; set; } 12 | string Filter { get; set; } 13 | bool IncludeSubdirectories { get; set; } 14 | int InternalBufferSize { get; set; } 15 | NotifyFilters NotifyFilter { get; set; } 16 | string Path { get; set; } 17 | 18 | event FileSystemEventHandler Changed; 19 | event FileSystemEventHandler Created; 20 | event FileSystemEventHandler Deleted; 21 | event RenamedEventHandler Renamed; 22 | event ErrorEventHandler Error; 23 | 24 | WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType); 25 | WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout); 26 | } 27 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Icon/noun_160432_cc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theXappy/FileSystemWatcherAlts/6e228ed78367cb7aca7e4aa90debb9d737584441/FileSystemWatcherAlts/Icon/noun_160432_cc.png -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Polling/FileSystemPoller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.ComponentModel; 5 | using System.Diagnostics; 6 | using System.IO; 7 | using System.Text.RegularExpressions; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using FileSystemWatcherAlts.Utils.Extentions; 11 | using PathClass = System.IO.Path; 12 | 13 | namespace FileSystemWatcherAlts.Polling 14 | { 15 | [DebuggerDisplay("Path = {_path}, Filter = {_filter}, Polling = {_pollingTask!=null}, EnableRaisingEvents = {_enableRaisingEvents}")] 16 | /// 17 | /// Polls the file system and raises events when a directory, or file in a directory, changes. 18 | /// 19 | public class FileSystemPoller : IFileSystemWatcher, IFileSystemPoller 20 | { 21 | #region Events 22 | 23 | public event FileSystemEventHandler Created; 24 | public event FileSystemEventHandler Deleted; 25 | public event ErrorEventHandler Error; 26 | 27 | event FileSystemEventHandler IFileSystemWatcher.Changed 28 | { 29 | add 30 | { 31 | if(_supressNotSuppotedErrors) return; 32 | 33 | throw new NotImplementedException("Changed events are not supported by FileSystemPoller. If you are trying to wrap the poller use poller.SupressNotSupportedErrors = true to stop this exception from being thrown."); 34 | } 35 | remove 36 | { 37 | } 38 | } 39 | event RenamedEventHandler IFileSystemWatcher.Renamed 40 | { 41 | add 42 | { 43 | if (_supressNotSuppotedErrors) return; 44 | 45 | throw new NotImplementedException("Renamed events are not supported by FileSystemPoller. If you are trying to wrap the poller use poller.SupressNotSupportedErrors = true to stop this exception from being thrown."); 46 | } 47 | remove 48 | { 49 | } 50 | } 51 | 52 | #endregion 53 | 54 | #region Fields 55 | 56 | // classic watcher properties' backing fields 57 | /// 58 | /// Indicates whether the poller should raise it's events when a new notification is found 59 | /// 60 | private bool _enableRaisingEvents; 61 | /// 62 | /// Indicates what path of the directory the poller should poll from 63 | /// 64 | private string _path; 65 | /// 66 | /// An uppercase version of _path. used for string comparisons (prevents multiple ToUpper calls) 67 | /// 68 | private string _uppercasePath; 69 | /// 70 | /// Indicates whether the poller should supress it's NotSupported exceptions when subscribing to Changed/Renamed events. 71 | /// 72 | private bool _supressNotSuppotedErrors = false; 73 | /// 74 | /// Indicates whether the poller should poll subdirectories or not. 75 | /// 76 | private bool _includeSubdirectories; 77 | 78 | 79 | // basic file watching fields 80 | 81 | /// 82 | /// A FileSystemWatcher-like filter for the poller to use 83 | /// 84 | private string _filter; 85 | /// 86 | /// A regex expression created according to the _filter and used to check polled files. 87 | /// 88 | private Regex _regexFilter; 89 | /// 90 | /// Used by the polling thread to signal it has finished the initial polling. 91 | /// 92 | private readonly ManualResetEvent _initialFilesSeen; 93 | /// 94 | /// Collection of files seen in the last poll 95 | /// 96 | private IEnumerable _lastSeenFiles; 97 | /// 98 | /// Collection of directories seen in the last poll 99 | /// 100 | private IEnumerable _lastSeenDirs; 101 | 102 | 103 | // Polling related fields 104 | 105 | /// 106 | /// The task responsible for polling. 107 | /// 108 | private Task _pollingTask; 109 | /// 110 | /// Makes sure only a single thread starts/stops the polling task 111 | /// 112 | private readonly object _pollingTaskLock; 113 | /// 114 | /// Used by the polling thread to wait the timeout between polls. If set the thread stops waiting and continues. 115 | /// 116 | private readonly AutoResetEvent _pollingTimeoutEvent; 117 | /// 118 | /// Used to signal to the polling thread that it should stop execution 119 | /// 120 | private readonly ManualResetEventSlim _pollingEnabledEvent; 121 | /// 122 | /// Used by the polling thread to signal a poll was done sucessfully. The event is set afte EVERY poll. 123 | /// 124 | private readonly ManualResetEventSlim _pollDone; 125 | 126 | 127 | // WaitForChange fields 128 | 129 | /// 130 | /// Contains the number of threads waiting for notifications using the WaitForChanged methods 131 | /// 132 | private int _waiters; 133 | /// 134 | /// Used by the polling thread to signal to the waiters that a new notification is available. 135 | /// 136 | private readonly AutoResetEvent _changesWatchingEvent; 137 | /// 138 | /// Latest notification available for the WaitForChanged waiters 139 | /// 140 | private WaitForChangedResult _latestChange; 141 | /// 142 | /// Used to assert only a single thread (waiter/poller) access the _latestChange field at a time. 143 | /// 144 | private readonly object _latestChangeLocker; 145 | 146 | #endregion 147 | 148 | #region Properties 149 | 150 | /// 151 | /// Defines whether the poller acts as a 'Watcher' or as a clasic 'Poller'. 152 | /// 153 | public PollingType PollingType { get; set; } 154 | 155 | /// 156 | /// Path of the directory to monitor 157 | /// 158 | public string Path 159 | { 160 | get { return _path; } 161 | set 162 | { 163 | if (value == null) throw new NullReferenceException(nameof(Path)); 164 | if (value.Length == 0) throw new ArgumentException("Path cannot be an empty string.", nameof(Path)); 165 | 166 | StopPollingTask(); 167 | 168 | // Add the directory seperator character to the value if it's missing 169 | if (value[value.Length-1] != PathClass.DirectorySeparatorChar) 170 | { 171 | value = value + PathClass.DirectorySeparatorChar; 172 | } 173 | 174 | _path = value; 175 | _uppercasePath = _path.ToUpperInvariant(); 176 | 177 | StartPollingTask(); 178 | } 179 | } 180 | /// 181 | /// Whether the poller should raise Created/Deleted/Error events 182 | /// 183 | public bool EnableRaisingEvents 184 | { 185 | get { return _enableRaisingEvents; } 186 | set 187 | { 188 | if (String.IsNullOrEmpty(_path)) 189 | { 190 | throw new InvalidOperationException("No directory path was provided to the poller. Can not poll."); 191 | } 192 | if (!Directory.Exists(_path)) 193 | { 194 | throw new InvalidOperationException("Directory path to poll does not exist. Path: "+_path); 195 | } 196 | 197 | if (value) // settings raising to true 198 | { 199 | _initialFilesSeen.WaitOne(); // waiting for intialization to end 200 | } 201 | _enableRaisingEvents = value; 202 | } 203 | } 204 | 205 | /// 206 | /// A file name filter to monitor the directory with. Files/Directories which does not pass the filter won't be reported. 207 | /// 208 | public string Filter 209 | { 210 | get { return _filter; } 211 | set 212 | { 213 | _filter = value; 214 | 215 | if (value == string.Empty) 216 | { 217 | _regexFilter = new Regex(".*"); 218 | } 219 | else 220 | { 221 | // Turning a filesytsemwatcher filter into a regex filter 222 | // abc?.txt -> abc.\.txt 223 | // def*.bin -> def.*\.bin 224 | // *.txt -> .*\.txt 225 | // *.* -> .*\..* 226 | _regexFilter = new Regex(Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*")); 227 | } 228 | 229 | } 230 | } 231 | 232 | /// 233 | /// Whether the poller should also poll in subdirectories of the directory at Path 234 | /// 235 | public bool IncludeSubdirectories 236 | { 237 | get { return _includeSubdirectories; } 238 | set 239 | { 240 | StopPollingTask(); 241 | 242 | _includeSubdirectories = value; 243 | 244 | StartPollingTask(); 245 | } 246 | } 247 | 248 | int IFileSystemWatcher.InternalBufferSize { get; set; } 249 | NotifyFilters IFileSystemWatcher.NotifyFilter { get; set; } 250 | 251 | /// 252 | /// The interval to poll at. 253 | /// 254 | public int PollingInterval { get; set; } 255 | 256 | /// 257 | /// Prevents the "NotImplementedException" from being thrown when subscribing to the Renamed/Changed events. 258 | /// Set this to true when trying to wrap the Poller. The events will still not invoke but will allow subscription. 259 | /// 260 | [Browsable(false)] 261 | public bool SupressNotSupportedErrors 262 | { 263 | get { return _supressNotSuppotedErrors; } 264 | set { _supressNotSuppotedErrors=value; } 265 | } 266 | 267 | /// 268 | /// Whether any threads are currently waiting in one of the WaitForChanged overloads 269 | /// 270 | private bool ReportsExpected => Volatile.Read(ref _waiters) != 0 || EnableRaisingEvents; 271 | 272 | #endregion 273 | 274 | #region Constructors 275 | 276 | public FileSystemPoller(int pollingInterval) 277 | { 278 | _path = String.Empty; 279 | 280 | _initialFilesSeen = new ManualResetEvent(false); 281 | _lastSeenFiles = new HashSet(); 282 | _lastSeenDirs = new HashSet(); 283 | 284 | Filter = string.Empty; 285 | 286 | _waiters = 0; 287 | _changesWatchingEvent = new AutoResetEvent(false); 288 | _latestChangeLocker = new object(); 289 | _latestChange = new WaitForChangedResult(); 290 | 291 | _pollingTaskLock = new object(); 292 | PollingInterval = pollingInterval; 293 | _pollDone = new ManualResetEventSlim(false); 294 | _pollingTimeoutEvent = new AutoResetEvent(false); 295 | _pollingEnabledEvent = new ManualResetEventSlim(true); 296 | 297 | PollingType = PollingType.Watch; 298 | 299 | } 300 | 301 | public FileSystemPoller(int pollingInterval, string path) : this(pollingInterval) 302 | { 303 | Path = path; 304 | StartPollingTask(); 305 | } 306 | 307 | public FileSystemPoller(int pollingInterval, string path, string filter) : this(pollingInterval, path) 308 | { 309 | Filter = filter; 310 | StartPollingTask(); 311 | } 312 | 313 | private void StopPollingTask() 314 | { 315 | // Check if a polling task even exist 316 | if (_pollingTask == null) return; 317 | 318 | lock (_pollingTaskLock) 319 | { 320 | if (_pollingTask == null) return; 321 | 322 | _initialFilesSeen.Set(); 323 | _pollingEnabledEvent.Reset(); // Signaling for the task to quit 324 | _pollingTimeoutEvent.Set(); // Trying to speed up the task exit by interupting the 'sleep' period 325 | if (_pollingTask.Status == TaskStatus.Running) 326 | { 327 | _pollingTask.Wait(); 328 | } 329 | _pollingTask = null; 330 | } 331 | } 332 | 333 | private void StartPollingTask() 334 | { 335 | // Check if a no other polling task exists 336 | if (_pollingTask != null) return; 337 | 338 | lock (_pollingTaskLock) 339 | { 340 | if (_pollingTask != null) return; 341 | 342 | _initialFilesSeen.Reset(); 343 | _pollingTimeoutEvent.Reset(); 344 | _pollingEnabledEvent.Set(); 345 | _lastSeenFiles = new Collection(); 346 | _lastSeenDirs = new Collection(); 347 | _pollingTask = Task.Factory.StartNew(Poll, TaskCreationOptions.LongRunning); 348 | } 349 | } 350 | 351 | #endregion 352 | 353 | #region Polling Methods 354 | 355 | /// 356 | /// Polls the files currently in the directory 357 | /// 358 | private void PollInitialDirContent() 359 | { 360 | // Get initial content 361 | while (!_initialFilesSeen.WaitOne(1)) 362 | { 363 | // Check if polling was disabled 364 | if (!_pollingEnabledEvent.Wait(1)) return; 365 | 366 | // Query files in folder 367 | IEnumerable currentFiles; 368 | IEnumerable currentFolders; 369 | if (PollCurrentFiles(out currentFiles) && PollCurrentSubDirs(out currentFolders)) 370 | { 371 | _lastSeenFiles = currentFiles; 372 | _lastSeenDirs = currentFolders; 373 | _initialFilesSeen.Set(); 374 | return; 375 | } 376 | else 377 | { 378 | 379 | } 380 | 381 | // Check if polling was disabled 382 | if (!_pollingEnabledEvent.Wait(1)) return; 383 | 384 | // Sleep 385 | _pollingTimeoutEvent.WaitOne(PollingInterval); 386 | } 387 | } 388 | 389 | /// 390 | /// Constantly polls the files in the path given. 391 | /// 392 | private void Poll() 393 | { 394 | 395 | // Firstly, get an idea of what the folder currently looks like. 396 | PollInitialDirContent(); 397 | 398 | 399 | while (true) 400 | { 401 | // Check if polling was disabled 402 | if (!_pollingEnabledEvent.Wait(1)) break; 403 | 404 | // Sleep 405 | _pollingTimeoutEvent.WaitOne(PollingInterval); 406 | 407 | // Check if polling was disabled while waiting the timeout (which might be long) 408 | if (!_pollingEnabledEvent.Wait(1)) break; 409 | 410 | // Poll both files and directories in watched folder 411 | IEnumerable currentFiles; 412 | IEnumerable currentFolders; 413 | if (!PollCurrentFiles(out currentFiles) || !PollCurrentSubDirs(out currentFolders)) 414 | { 415 | 416 | // Polling files or folders failed, continuing to next sleep 417 | continue; 418 | } 419 | 420 | ProcessPolledItems(currentFiles, currentFolders); 421 | 422 | // Inform any 'ForcePoll' threads that the poll finished 423 | _pollDone.Set(); 424 | } 425 | 426 | } 427 | 428 | /// 429 | /// Proccess collections of files and folders currently polled and runs checks on them according to the polling type 430 | /// 431 | /// Files that currently exist under the polled folder 432 | /// Folders that currently exist under the polled folder 433 | private void ProcessPolledItems(IEnumerable currentFiles, IEnumerable currentFolders) 434 | { 435 | // Orginazing possible check to run each poll 436 | List actionsOnItems; 437 | if (PollingType == PollingType.Watch) 438 | { 439 | actionsOnItems = new List() 440 | { 441 | () => ReportCreatedItems(_lastSeenFiles, currentFiles), // Check for new files 442 | () => ReportCreatedItems(_lastSeenDirs, currentFolders), // Check for new folders 443 | () => ReportDeletedItems(_lastSeenFiles, currentFiles), // Check for deleted files 444 | () => ReportDeletedItems(_lastSeenDirs, currentFolders) // Check for deleted folders 445 | }; 446 | } 447 | else // PollingType == PollingType.Poll 448 | { 449 | actionsOnItems = new List() 450 | { 451 | () => ReportItems(currentFiles,WatcherChangeTypes.Created), // Report current files that match the filter 452 | () => ReportItems(currentFolders,WatcherChangeTypes.Created), // Report current directories that match the filter 453 | }; 454 | } 455 | 456 | // For each one of the checks above, see if there is a point even running this check (EnableRaisingEvents is true or threads are WaitingForChange-s). 457 | foreach (Action itemsCheck in actionsOnItems) 458 | { 459 | if (ReportsExpected) 460 | { 461 | itemsCheck(); 462 | } 463 | } 464 | 465 | // Update "last seen files" and "last seen folders" 466 | _lastSeenFiles = currentFiles; 467 | _lastSeenDirs = currentFolders; 468 | } 469 | 470 | /// 471 | /// Forces the Poller to poll for files immediatly. 472 | /// 473 | public Task ForcePollAsync(bool returnWhenPolled = false) 474 | { 475 | return Task.Factory.StartNew(()=> ForcePoll(returnWhenPolled)); 476 | } 477 | 478 | /// 479 | /// Forces the Poller to poll for files immediatly. 480 | /// 481 | public void ForcePoll() 482 | { 483 | ForcePoll(returnWhenPolled: true); 484 | } 485 | 486 | /// 487 | /// Forces the Poller to poll for files immediatly. 488 | /// 489 | public void ForcePoll(bool returnWhenPolled) 490 | { 491 | _pollingTimeoutEvent.Set(); 492 | if (returnWhenPolled) 493 | { 494 | _pollDone.Reset(); 495 | _pollDone.Wait(); 496 | } 497 | } 498 | 499 | /// 500 | /// Polls a collection of file names in the watched folder 501 | /// 502 | /// Output variable for the files' names 503 | /// True if polling succeeded, false otherwise 504 | private bool PollCurrentFiles(out IEnumerable currentFiles) 505 | { 506 | 507 | currentFiles = null; 508 | try 509 | { 510 | currentFiles = DirectoryExtentions.GetFilesInA>(Path, IncludeSubdirectories); 511 | 512 | return true; 513 | } 514 | catch (Exception ex) 515 | { 516 | 517 | OnError(new ErrorEventArgs(ex)); 518 | return false; 519 | } 520 | } 521 | 522 | /// 523 | /// Polls a collection of directories names in the watched folder 524 | /// 525 | /// Output variable for the directories' names 526 | /// True if polling succeeded, false otherwise 527 | private bool PollCurrentSubDirs(out IEnumerable currentFolders) 528 | { 529 | 530 | currentFolders = null; 531 | try 532 | { 533 | currentFolders = DirectoryExtentions.GetDirsInA>(Path, IncludeSubdirectories); 534 | 535 | return true; 536 | } 537 | catch (Exception ex) 538 | { 539 | 540 | OnError(new ErrorEventArgs(ex)); 541 | return false; 542 | } 543 | } 544 | 545 | #endregion 546 | 547 | #region Files Examination Methods 548 | 549 | /// 550 | /// Compares an old and a new collection of files to see if any old items were removed. Pops the "Deleted" event for each of those items. 551 | /// 552 | /// Set of old items 553 | /// Set of new items 554 | private void ReportDeletedItems(IEnumerable originalItems, IEnumerable currentItems) 555 | { 556 | 557 | // Copy last known items to a new set 558 | ISet deletedFiles = new HashSet(originalItems); 559 | // Substract current items 560 | deletedFiles.ExceptWith(currentItems); 561 | 562 | // Runs the items through the filter and reports matching ones 563 | ReportItems(deletedFiles, WatcherChangeTypes.Deleted); 564 | } 565 | 566 | /// 567 | /// Compares an old and a new collection of files to see if any new items were added. Pops the "Created" event for each of those items. 568 | /// 569 | /// Set of old items 570 | /// Set of new items 571 | private void ReportCreatedItems(IEnumerable originalItems, IEnumerable currentItems) 572 | { 573 | 574 | // Copy current found items to a new set 575 | ISet addedItems = new HashSet(currentItems); 576 | // Substract last seen items 577 | addedItems.ExceptWith(originalItems); 578 | 579 | // Runs the items through the filter and reports matching ones 580 | ReportItems(addedItems, WatcherChangeTypes.Created); 581 | } 582 | 583 | /// 584 | /// Checks an enumerable of items with the current filter and reports those who fit. 585 | /// 586 | /// The collection of items (files/folders) to check 587 | /// The type of report to create for those items 588 | private void ReportItems(IEnumerable items, WatcherChangeTypes reportType) 589 | { 590 | foreach (var item in items) 591 | { 592 | string itemName = PathClass.GetFileName(item); 593 | if (!PassesFilter(itemName)) continue; 594 | string folder = PathClass.GetDirectoryName(item) ?? string.Empty; 595 | 596 | SignalFileChangeForWaiters(reportType, item); 597 | 598 | if (EnableRaisingEvents) 599 | { 600 | FileSystemEventArgs reportArgs = new FileSystemEventArgs(reportType, folder, itemName); 601 | switch (reportType) 602 | { 603 | case WatcherChangeTypes.Created: 604 | OnCreated(reportArgs); 605 | break; 606 | case WatcherChangeTypes.Deleted: 607 | OnDeleted(reportArgs); 608 | break; 609 | } 610 | } 611 | } 612 | } 613 | 614 | /// 615 | /// Checks if a single file name/folder name matches the currently set filter 616 | /// 617 | /// File/Folder name 618 | /// True if the name matches the filter, false otherwise. 619 | private bool PassesFilter(string item) 620 | { 621 | // returns whether *the string is not empty* && *the string matches filter* 622 | return !String.IsNullOrEmpty(item) && _regexFilter.IsMatch(item); 623 | } 624 | 625 | #endregion 626 | 627 | #region Events Raising Methods 628 | 629 | private void OnCreated(FileSystemEventArgs fileSystemEventArgs) 630 | { 631 | 632 | Created?.Invoke(this, fileSystemEventArgs); 633 | } 634 | private void OnDeleted(FileSystemEventArgs fileSystemEventArgs) 635 | { 636 | 637 | Deleted?.Invoke(this, fileSystemEventArgs); 638 | } 639 | private void OnError(ErrorEventArgs errorEventArgs) 640 | { 641 | 642 | Error?.Invoke(this, errorEventArgs); 643 | } 644 | 645 | #endregion 646 | 647 | #region WaitForChanged Methods 648 | 649 | private void SignalFileChangeForWaiters(WatcherChangeTypes type, string filePath) 650 | { 651 | if (_waiters == 0) return; // No point signaling if no one is waiting 652 | 653 | // Getting the 'relative path' of the filePath compared to the currently monitored folder path 654 | string uppercaseFilePath = filePath.ToUpperInvariant(); 655 | var fileNameToReport = uppercaseFilePath.Replace(_uppercasePath, string.Empty); 656 | 657 | lock (_latestChangeLocker) 658 | { 659 | _latestChange = new WaitForChangedResult() { ChangeType = type, Name = fileNameToReport }; 660 | } 661 | _changesWatchingEvent.Set(); 662 | } 663 | 664 | public WaitForChangedResult WaitForChanged(WatcherChangeTypes type) 665 | { 666 | if (type == WatcherChangeTypes.Renamed || 667 | type == WatcherChangeTypes.Changed || 668 | type == (WatcherChangeTypes.Changed | WatcherChangeTypes.Renamed)) // Polling cannot monitor these changes 669 | { 670 | throw new NotImplementedException("File System Poller can not monitor \"Rename\" or \"Changed\" file changes."); 671 | } 672 | 673 | while (true) 674 | { 675 | Interlocked.Increment(ref _waiters); 676 | _changesWatchingEvent.WaitOne(); 677 | Interlocked.Decrement(ref _waiters); 678 | 679 | WaitForChangedResult results; 680 | lock (_latestChangeLocker) 681 | { 682 | results = _latestChange; 683 | } 684 | // Check if the report fits the one the current thread is looking for 685 | if (type.HasFlag(results.ChangeType)) 686 | { 687 | // It does, returning the report. 688 | return results; 689 | } 690 | else 691 | { 692 | // It doesn't. 693 | // allowing a signle other thread to examine it this report: 694 | _changesWatchingEvent.Set(); 695 | // making sure the event is reset when the current thread returns to it. (If a thread is waiting it will exit after the .Set and before the .Reset) 696 | _changesWatchingEvent.Reset(); 697 | } 698 | } 699 | } 700 | 701 | public WaitForChangedResult WaitForChanged(WatcherChangeTypes type, int timeout) 702 | { 703 | if (type == WatcherChangeTypes.Renamed || 704 | type == WatcherChangeTypes.Changed || 705 | type == (WatcherChangeTypes.Changed | WatcherChangeTypes.Renamed)) // Polling cannot monitor these changes 706 | { 707 | throw new NotImplementedException("File System Poller can not monitor \"Rename\" or \"Changed\" item changes."); 708 | } 709 | 710 | // Using this stopwatch to check I'm staying in the method longer then the timeout set 711 | Stopwatch timeInMethodStopwatch = Stopwatch.StartNew(); 712 | 713 | Interlocked.Increment(ref _waiters); 714 | while (true) 715 | { 716 | int remainingTimeToWait = timeout - (int)timeInMethodStopwatch.ElapsedMilliseconds; 717 | var timedOut = !_changesWatchingEvent.WaitOne(remainingTimeToWait); 718 | 719 | if (timedOut) // wait timed out, exit method. 720 | { 721 | Interlocked.Decrement(ref _waiters); 722 | return new WaitForChangedResult() { ChangeType = type, TimedOut = true }; 723 | } 724 | 725 | // wait didn't time out - check results 726 | WaitForChangedResult results; 727 | lock (_latestChangeLocker) 728 | { 729 | results = _latestChange; 730 | } 731 | // Check if the reported results match the requestsed result type. 732 | // Otherwise - continue waiting for more changes 733 | if (type.HasFlag(results.ChangeType)) 734 | { 735 | Interlocked.Decrement(ref _waiters); 736 | return results; 737 | } 738 | } 739 | } 740 | 741 | #endregion 742 | 743 | #region IDisposeable Methods 744 | 745 | public void Dispose() 746 | { 747 | // Canceling polling task 748 | StopPollingTask(); 749 | 750 | // Empty files/folders collections - those might get quite large 751 | _lastSeenFiles = new Collection(); 752 | _lastSeenDirs = new Collection(); 753 | } 754 | 755 | #endregion 756 | 757 | #region ICloneable Methods 758 | 759 | public object Clone() 760 | { 761 | var clonedPoller = new FileSystemPoller(this.PollingInterval, this.Path, this.Filter) 762 | { 763 | IncludeSubdirectories = this.IncludeSubdirectories, 764 | EnableRaisingEvents = this.EnableRaisingEvents, 765 | PollingType = this.PollingType 766 | }; 767 | 768 | return clonedPoller; 769 | } 770 | 771 | #endregion 772 | } 773 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Polling/IFileSystemPoller.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | 5 | namespace FileSystemWatcherAlts.Polling 6 | { 7 | public interface IFileSystemPoller : IDisposable, ICloneable 8 | { 9 | bool EnableRaisingEvents { get; set; } 10 | string Filter { get; set; } 11 | bool IncludeSubdirectories { get; set; } 12 | string Path { get; set; } 13 | int PollingInterval { get; set; } 14 | PollingType PollingType { get; set; } 15 | 16 | event FileSystemEventHandler Created; 17 | event FileSystemEventHandler Deleted; 18 | event ErrorEventHandler Error; 19 | 20 | Task ForcePollAsync(bool returnWhenPolled); 21 | void ForcePoll(); 22 | } 23 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Polling/PollingType.cs: -------------------------------------------------------------------------------- 1 | namespace FileSystemWatcherAlts.Polling 2 | { 3 | /// 4 | /// Defines how FileSystemPoller reports back to the listeners 5 | /// 6 | public enum PollingType 7 | { 8 | /// 9 | /// Watcher-like behivour. Reports NEWLY created/deleted files. 10 | /// 11 | Watch, 12 | /// 13 | /// Poller-like behivour. Reports ALL existing files in the directory in EVERY poll. 14 | /// 15 | Poll 16 | } 17 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("FileSystemWatcherAlts")] 8 | [assembly: AssemblyDescription("Alternatives for System.IO.FileSystemWatcher")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("Shai Shapira")] 11 | [assembly: AssemblyProduct("FileSystemWatcherAlts")] 12 | [assembly: AssemblyCopyright("Copyright © Shai Shapira 2015")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("195f032d-da21-40a0-abff-6fc4887a6839")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Utils/Extentions/CollectionsExtentions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace FileSystemWatcherAlts.Utils.Extentions 4 | { 5 | internal static class CollectionsExtentions 6 | { 7 | /// 8 | /// Pushes a range of items into a stack 9 | /// 10 | /// The type of items in the stack 11 | /// The stack to push into 12 | /// The items to push 13 | internal static void PushRange(this Stack stack, IEnumerable items) 14 | { 15 | foreach (var item in items) 16 | { 17 | stack.Push(item); 18 | } 19 | } 20 | 21 | /// 22 | /// Addsa a range of items into a collection 23 | /// 24 | /// The type of items in the collection 25 | /// The collection to add the items into 26 | /// The items to add 27 | internal static void AddRange(this ICollection collection, IEnumerable items) 28 | { 29 | foreach (var item in items) 30 | { 31 | collection.Add(item); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Utils/Extentions/DirectoryExtentions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | 5 | namespace FileSystemWatcherAlts.Utils.Extentions 6 | { 7 | internal static class DirectoryExtentions 8 | { 9 | /// 10 | /// Gets paths of the files under a directory in a given collection type. 11 | /// 12 | /// The type of strings collection to get the paths as. 13 | /// The directory to look for files. 14 | /// Whether to include sub-directories files or not. 15 | /// A collection of file paths found under . 16 | internal static T GetFilesInA(string directory, bool includeSubDirectories = false) where T : ICollection, new() 17 | { 18 | T output = new T(); 19 | output.AddRange(Directory.GetFiles(directory)); 20 | if (includeSubDirectories) 21 | { 22 | Stack subDirsStack = new Stack(Directory.GetDirectories(directory)); 23 | while (subDirsStack.Any()) 24 | { 25 | // Get next sub-dir 26 | string nextDir = subDirsStack.Pop(); 27 | // Get the dir's sub-dir and push them into the stack 28 | subDirsStack.PushRange(Directory.GetDirectories(nextDir)); 29 | // Get the files in the subfolder and union them with the currentFiles set 30 | var filesInSubfolder = Directory.GetFiles(nextDir); 31 | output.AddRange(filesInSubfolder); 32 | } 33 | } 34 | else 35 | { 36 | var filesInSubfolder = Directory.GetFiles(directory); 37 | output.AddRange(filesInSubfolder); 38 | } 39 | return output; 40 | } 41 | 42 | /// 43 | /// Gets paths of the sub-directories under a directory in a given collection type. 44 | /// 45 | /// The type of strings collection to get the paths as. 46 | /// The directory to look for sub-directories. 47 | /// Whether to include sub-directories' sub-directories or not. 48 | /// A collection of directories paths found under . 49 | internal static T GetDirsInA(string directory, bool includeSubDirectories = false) where T : ICollection, new() 50 | { 51 | T output = new T(); 52 | if (includeSubDirectories) 53 | { 54 | Stack subDirsStack = new Stack(Directory.GetDirectories(directory)); 55 | while (subDirsStack.Any()) 56 | { 57 | // Get next sub-dir 58 | string nextDir = subDirsStack.Pop(); 59 | IEnumerable nextSubDirs = Directory.GetDirectories(nextDir); 60 | // Get the dir's sub-dir and push them into the stack 61 | subDirsStack.PushRange(nextSubDirs); 62 | // Get the files in the subfolder and union them with the currentFiles set 63 | output.AddRange(nextSubDirs); 64 | } 65 | } 66 | else 67 | { 68 | IEnumerable nextSubDirs = Directory.GetDirectories(directory); 69 | output.AddRange(nextSubDirs); 70 | } 71 | return output; 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Utils/Extentions/ExceptionsExtentions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace FileSystemWatcherAlts.Utils.Extentions 5 | { 6 | internal static class ExceptionsExtentions 7 | { 8 | /// 9 | /// Extracts the path of the directory in the DirectoryNotFoundException 10 | /// 11 | /// The exception 12 | /// The path of the directory 13 | internal static string Path(this DirectoryNotFoundException ex) 14 | { 15 | return GetPathFromMessage(ex.Message); 16 | } 17 | 18 | /// 19 | /// Extracts the path of the directory in the UnauthorizedAccessException 20 | /// 21 | /// The exception 22 | /// The path of the directory 23 | internal static string Path(this UnauthorizedAccessException ex) 24 | { 25 | return GetPathFromMessage(ex.Message); 26 | } 27 | 28 | private static string GetPathFromMessage(string exMessage) 29 | { 30 | int startIndex = exMessage.IndexOf('\'') + 1; 31 | int endIndex = exMessage.LastIndexOf('\''); 32 | int length = endIndex - startIndex; 33 | 34 | // Here I assert that atleast 2 apostrophe exist in the message 35 | if (length < 0) return string.Empty; 36 | 37 | return exMessage.Substring(startIndex, length); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Utils/FileSystemFakeWatcher.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace FileSystemWatcherAlts.Utils 4 | { 5 | /// 6 | /// A fake object which implements the IFileSystemWatcher interface. 7 | /// 8 | internal class FileSystemFakeWatcher : IFileSystemWatcher 9 | { 10 | #region Properties 11 | 12 | public bool EnableRaisingEvents { get; set; } 13 | public string Filter { get; set; } 14 | public bool IncludeSubdirectories { get; set; } 15 | public int InternalBufferSize { get; set; } 16 | public NotifyFilters NotifyFilter { get; set; } 17 | public string Path { get; set; } 18 | 19 | #endregion 20 | 21 | #region Events 22 | 23 | public event FileSystemEventHandler Changed 24 | { 25 | add { } 26 | remove { } 27 | } 28 | 29 | public event FileSystemEventHandler Created 30 | { 31 | add { } 32 | remove { } 33 | } 34 | 35 | public event FileSystemEventHandler Deleted 36 | { 37 | add { } 38 | remove { } 39 | } 40 | 41 | public event RenamedEventHandler Renamed 42 | { 43 | add { } 44 | remove { } 45 | } 46 | 47 | public event ErrorEventHandler Error 48 | { 49 | add { } 50 | remove { } 51 | } 52 | 53 | #endregion 54 | 55 | #region IFileSystemWatcher Methods 56 | 57 | public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) 58 | { 59 | return new WaitForChangedResult(); 60 | } 61 | 62 | public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) 63 | { 64 | return new WaitForChangedResult(); 65 | } 66 | 67 | #endregion 68 | 69 | #region IDisposable Methods 70 | 71 | public void Dispose() 72 | { 73 | } 74 | 75 | #endregion 76 | 77 | #region ICloneable Methods 78 | 79 | public object Clone() 80 | { 81 | return this; 82 | } 83 | 84 | #endregion 85 | } 86 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Utils/WatcherErrorHandlingPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FileSystemWatcherAlts.Utils 4 | { 5 | /// 6 | /// Defines a policy for handling an error a FileSystemWatcher might report. 7 | /// 8 | public struct WatcherErrorHandlingPolicy 9 | { 10 | #region Properties 11 | 12 | /// 13 | /// The type of exceptions this policy is testing 14 | /// 15 | public Type ExceptionType { get; set; } 16 | 17 | /// 18 | /// A description about the policy 19 | /// 20 | public string Description { get; set; } 21 | 22 | /// 23 | /// A test to run for each exception of type to determine how the error should be handled. 24 | /// 25 | public Func Test { get; set; } 26 | 27 | #endregion 28 | 29 | #region Constructor 30 | 31 | /// 32 | /// Constructor 33 | /// 34 | /// The exception type to enforce the policy on 35 | /// Literal descirption of the policy 36 | /// A test to run for each exception of type to determine how the error should be handled. 37 | public WatcherErrorHandlingPolicy(Type exceptionType, string description, 38 | Func test) 39 | { 40 | ExceptionType = exceptionType; 41 | Description = description; 42 | Test = test; 43 | } 44 | 45 | #endregion 46 | } 47 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Utils/WatcherErrorHandlingType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FileSystemWatcherAlts.Utils 4 | { 5 | /// 6 | /// Different approachs to handle a FileSystemWatcher error. 7 | /// 8 | [Flags] 9 | public enum WatcherErrorHandlingType 10 | { 11 | /// 12 | /// Forward the error using the Error event 13 | /// 14 | Forward = 0, 15 | /// 16 | /// Do not forward the error using the Error event 17 | /// 18 | Swallow = 1, 19 | /// 20 | /// Refresh the internal watcher 21 | /// 22 | Refresh = 2, 23 | /// 24 | /// Refresh the internal watcher and do not forward the error using the Error event 25 | /// 26 | RefreshAndSwallow = 3, 27 | } 28 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Wrappers/FileSystemAutoRefreshingWatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.IO; 5 | using System.Linq; 6 | using FileSystemWatcherAlts.Utils; 7 | 8 | namespace FileSystemWatcherAlts.Wrappers 9 | { 10 | /// 11 | /// An IFileSystemWatcher wrapper which automaticly refreshes it when specific errors occurre. 12 | /// 13 | public class FileSystemAutoRefreshingWatcher : FileSystemRefreshableWatcher 14 | { 15 | #region Fields 16 | 17 | private List _errorHandlingPolicies; 18 | 19 | #endregion 20 | 21 | #region Properties 22 | 23 | public IReadOnlyCollection ErrorHandlingPolicies => _errorHandlingPolicies; 24 | 25 | #endregion 26 | 27 | #region Constructor 28 | 29 | public FileSystemAutoRefreshingWatcher() 30 | { 31 | InitBasicPolicies(); 32 | } 33 | 34 | public FileSystemAutoRefreshingWatcher(FileSystemWatcher watcher) : base(watcher) 35 | { 36 | InitBasicPolicies(); 37 | } 38 | 39 | public FileSystemAutoRefreshingWatcher(IFileSystemWatcher watcher) : base(watcher) 40 | { 41 | InitBasicPolicies(); 42 | } 43 | 44 | public FileSystemAutoRefreshingWatcher(string path) : base(path) 45 | { 46 | InitBasicPolicies(); 47 | } 48 | 49 | public FileSystemAutoRefreshingWatcher(string path, string filter) : base(path, filter) 50 | { 51 | InitBasicPolicies(); 52 | } 53 | 54 | public void InitBasicPolicies() 55 | { 56 | _errorHandlingPolicies = new List(); 57 | 58 | var accessDeniedPolicy = new WatcherErrorHandlingPolicy( 59 | typeof (Win32Exception), 60 | "When an 'access denied' win32 exception occures, refresh the wrapped watcher.", 61 | exception => 62 | (exception as Win32Exception)?.NativeErrorCode == 5 ? 63 | WatcherErrorHandlingType.RefreshAndSwallow : 64 | WatcherErrorHandlingType.Forward); 65 | 66 | var netNameDeletedPolicy = new WatcherErrorHandlingPolicy( 67 | typeof (Win32Exception), 68 | "When a 'net name deleted' win32 exception occures, refresh the wrapped watcher.", 69 | exception => 70 | (exception as Win32Exception)?.NativeErrorCode == 64 ? 71 | WatcherErrorHandlingType.RefreshAndSwallow : 72 | WatcherErrorHandlingType.Forward); 73 | 74 | _errorHandlingPolicies.Add(accessDeniedPolicy); 75 | _errorHandlingPolicies.Add(netNameDeletedPolicy); 76 | } 77 | 78 | #endregion 79 | 80 | #region Methods 81 | 82 | /// 83 | /// Removes all currently set error handling policies 84 | /// 85 | public void ClearPolicies() 86 | { 87 | _errorHandlingPolicies.Clear(); 88 | } 89 | 90 | /// 91 | /// Tries to remove a specific error handling policy 92 | /// 93 | /// The policy to remove 94 | /// 95 | /// true if policy is successfully removed; otherwise, false. This method also returns 96 | /// false if policy was not found in the policies collection. 97 | /// 98 | public bool RemovePolicy(WatcherErrorHandlingPolicy pol) 99 | { 100 | return _errorHandlingPolicies.Remove(pol); 101 | } 102 | 103 | /// 104 | /// Adds an error handling policy 105 | /// 106 | /// 107 | public void AddPolicy(WatcherErrorHandlingPolicy pol) 108 | { 109 | _errorHandlingPolicies.Add(pol); 110 | } 111 | 112 | /// 113 | /// Inoked when the wrapped watcher throws an exception. The exception is tested with the existing policies 114 | /// and handled according to the tests results. 115 | /// 116 | /// Raiser of the event 117 | /// Error event args 118 | protected override void OnError(object sender, ErrorEventArgs e) 119 | { 120 | Exception ex = e.GetException(); 121 | Type exType = ex.GetType(); 122 | WatcherErrorHandlingType exHandling = WatcherErrorHandlingType.Forward; 123 | 124 | // Testing all relevant policies according to the exception type 125 | foreach (var relevantPolicy in ErrorHandlingPolicies.Where(policy => policy.ExceptionType == exType)) 126 | { 127 | exHandling |= relevantPolicy.Test(ex); 128 | } 129 | 130 | // Check the policies test results. 131 | 132 | // If ANY of the policies requested a refresh - a refresh will be invoked 133 | if (exHandling.HasFlag(WatcherErrorHandlingType.Refresh)) 134 | { 135 | // Tries to refresh. If a refresh is already in progress, the thread returns. 136 | var refreshTask = RefreshAsync(returnWhenRefreshed: false); 137 | } 138 | 139 | // If NONE of the policies requested a swallow - the error will be forwarded 140 | // (if any of them DID request a swallow, the error will be swallowed) 141 | if (!exHandling.HasFlag(WatcherErrorHandlingType.Swallow)) 142 | { 143 | base.OnError(sender, e); 144 | } 145 | } 146 | 147 | #endregion 148 | 149 | #region ICloneable Methods 150 | 151 | public override object Clone() 152 | { 153 | IFileSystemWatcher clonedEncapsWatcher = InternalWatcher.Clone() as IFileSystemWatcher; 154 | FileSystemAutoRefreshingWatcher clonedAutoRefreshingWatcher = new FileSystemAutoRefreshingWatcher(clonedEncapsWatcher); 155 | // Add current refresher's policies to the cloned one 156 | clonedAutoRefreshingWatcher.ClearPolicies(); 157 | foreach (var policy in _errorHandlingPolicies) 158 | { 159 | clonedAutoRefreshingWatcher.AddPolicy(policy); 160 | } 161 | return clonedAutoRefreshingWatcher; 162 | } 163 | 164 | #endregion 165 | } 166 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Wrappers/FileSystemRefreshableWatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.ComponentModel; 4 | using System.IO; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using FileSystemWatcherAlts.Utils; 8 | using Polly; 9 | 10 | namespace FileSystemWatcherAlts.Wrappers 11 | { 12 | /// 13 | /// An IFileSystemWatcher wrapper which allows refreshing the watcher for when it ceases to work due to a problem. 14 | /// 15 | public class FileSystemRefreshableWatcher : FileSystemWatcherWrapper 16 | { 17 | #region Fields 18 | 19 | /// 20 | /// A collection of ManualResetEvents of the threads currently waiting for a refresh to finish 21 | /// 22 | private readonly ConcurrentDictionary _waitingThreadsEvents; 23 | /// 24 | /// Used to synchronize between different threads that try to refresh the watcher at the same time 25 | /// Only the one who successfully entered this object is allowed to refresh. 26 | /// 27 | private readonly object _refreshLock; 28 | 29 | private readonly CancellationTokenSource _refreshTokenSource; 30 | 31 | #endregion 32 | 33 | #region Properties 34 | 35 | /// 36 | /// The amount of time in milliseconds to wait between refresh attemps on the watcher. 37 | /// 38 | [Browsable(false)] 39 | public int RefreshAttempInterval { get; set; } = 500; 40 | 41 | /// 42 | /// Wether the watcher is currently refreshing or not. 43 | /// 44 | public bool IsRefreshing { get; private set; } 45 | 46 | #endregion 47 | 48 | #region Events 49 | 50 | public event EventHandler Refreshed; 51 | 52 | #endregion 53 | 54 | #region Constructors 55 | 56 | public FileSystemRefreshableWatcher(IFileSystemWatcher watcher) : base(watcher) 57 | { 58 | _refreshTokenSource = new CancellationTokenSource(); 59 | _waitingThreadsEvents = new ConcurrentDictionary(); 60 | IsRefreshing = false; 61 | _refreshLock = new object(); 62 | } 63 | public FileSystemRefreshableWatcher(FileSystemWatcher watcher) : this(new FileSystemWatcherAdapter(watcher)) 64 | { 65 | } 66 | public FileSystemRefreshableWatcher() : this(new FileSystemWatcher()) 67 | { 68 | } 69 | public FileSystemRefreshableWatcher(string path) : this(new FileSystemWatcher(path)) 70 | { 71 | } 72 | public FileSystemRefreshableWatcher(string path, string filter) : this(new FileSystemWatcher(path, filter)) 73 | { 74 | } 75 | 76 | #endregion 77 | 78 | #region Methods 79 | 80 | /// 81 | /// Refreshes the internal FileSystemWatcher asynchronously 82 | /// 83 | /// 84 | public Task RefreshAsync(bool returnWhenRefreshed = true) 85 | { 86 | return Task.Factory.StartNew(()=>Refresh(returnWhenRefreshed)); 87 | } 88 | 89 | /// 90 | /// Refreshes the internal FileSystemWatcher 91 | /// 92 | public void Refresh() 93 | { 94 | // when using this synchronous method, the call should make sure to return only when the watcher has been refreshed. 95 | Refresh(returnWhenRefreshed: true); 96 | } 97 | 98 | /// 99 | /// Refreshes the internal FileSystemWatcher 100 | /// 101 | /// In case another thread is alreayd refreshing, determines wether the thread should return before the refreshing thread finishes or not. 102 | private void Refresh(bool returnWhenRefreshed) 103 | { 104 | // Making sure another thread isn't already refreshing: 105 | if (!Monitor.TryEnter(_refreshLock)) 106 | { 107 | // if another thread IS already refreshing - wait for it to finish then return 108 | if (returnWhenRefreshed) 109 | { 110 | WaitForRefresh(); 111 | } 112 | return; 113 | } 114 | IsRefreshing = true; 115 | 116 | // 1. unsubscribe from old watcher's events. 117 | UnsubscribeFromInternalWatcherEvents(); 118 | 119 | // 2a. Keeping the current internal "EnableRaisingEvents" value 120 | bool currentEnableRaisingEvents = InternalWatcher.EnableRaisingEvents; 121 | // 2b. Turning off EnableRaisingEvents to avoid "locking" the watched folder 122 | InternalWatcher.EnableRaisingEvents = false; 123 | 124 | // 3. Get a new watcher 125 | IFileSystemWatcher newInternalWatcher = GetReplacementWatcher(); 126 | newInternalWatcher.EnableRaisingEvents = currentEnableRaisingEvents; 127 | 128 | // 4. Disposing of the old watcher 129 | InternalWatcher.Dispose(); 130 | 131 | // 5. Place new watcher in the Internal watcher property 132 | // This also registers to the watcher's events 133 | InternalWatcher = newInternalWatcher; 134 | 135 | // Change state back to "not refreshing" 136 | IsRefreshing = false; 137 | // Notify any waiting threads that the refresh is done 138 | foreach (var waitingThreadEvent in _waitingThreadsEvents.Values) 139 | { 140 | waitingThreadEvent.Set(); 141 | } 142 | _waitingThreadsEvents.Clear(); 143 | Monitor.Exit(_refreshLock); 144 | 145 | // Notify listeners about the refresh. 146 | Refreshed?.Invoke(this, new EventArgs()); 147 | } 148 | 149 | /// 150 | /// Gets a replacement for the InternalWatcher 151 | /// 152 | /// A new IFileSystemWatcher of the same type as the InternalWatcher 153 | private IFileSystemWatcher GetReplacementWatcher() 154 | { 155 | IFileSystemWatcher newInternalWatcher = null; 156 | // Swallowing any exceptions that might occure when trying to get a clone of the current watcher 157 | CancellationToken cToken = _refreshTokenSource.Token; 158 | Policy.Handle() 159 | .RetryForever((ex, con) => Thread.Sleep(RefreshAttempInterval)) 160 | .Execute(() => 161 | { 162 | // If the refreshment is cancelled, place a fake as the new watcher and return. 163 | if (cToken.IsCancellationRequested) 164 | { 165 | newInternalWatcher = new FileSystemFakeWatcher(); 166 | return; //Exits polly's 'Execute' method. 167 | } 168 | 169 | newInternalWatcher = (IFileSystemWatcher) InternalWatcher.Clone(); 170 | // setting EnableRaisingEvents to true is where exceptions may raise so 171 | // I'm giving this clone a "test drive" before returning it to the Refresh method 172 | newInternalWatcher.EnableRaisingEvents = true; 173 | newInternalWatcher.EnableRaisingEvents = false; 174 | }); 175 | 176 | return newInternalWatcher; 177 | } 178 | 179 | /// 180 | /// Blocks the thread while a refresh is in progress 181 | /// 182 | public void WaitForRefresh() 183 | { 184 | // Create a reset event and adds it to the waiting threads events list 185 | ManualResetEventSlim refreshEvent = new ManualResetEventSlim(false); 186 | _waitingThreadsEvents[Thread.CurrentThread] = refreshEvent; 187 | refreshEvent.Wait(); 188 | } 189 | 190 | /// 191 | /// Blocks the thread while a refresh is in progress 192 | /// 193 | /// Maximum amount of time, in ms, to wait for the refresh to finish. 194 | /// True if the refresh finished in time, false if the wait timed out. 195 | public bool WaitForRefresh(int timeout) 196 | { 197 | // Create a reset event and adds it to the waiting threads events list 198 | ManualResetEventSlim refreshEvent = new ManualResetEventSlim(false); 199 | _waitingThreadsEvents[Thread.CurrentThread] = refreshEvent; 200 | 201 | var refreshed = refreshEvent.Wait(timeout); // waiting for the refresh 202 | 203 | if (!refreshed) // = wait timed out 204 | { 205 | // remove the event from the list 206 | _waitingThreadsEvents.TryRemove(Thread.CurrentThread, out refreshEvent); 207 | } 208 | return refreshed; 209 | } 210 | 211 | #endregion 212 | 213 | #region IDisposeable Methods 214 | 215 | ~FileSystemRefreshableWatcher() 216 | { 217 | Dispose(false); 218 | } 219 | 220 | public new void Dispose() 221 | { 222 | Dispose(true); 223 | GC.SuppressFinalize(this); 224 | } 225 | 226 | private void Dispose(bool disposing) 227 | { 228 | // If the refresher is currently refreshing, cancel the refresh and wait for the refreshing thread to exit 229 | if (IsRefreshing) 230 | { 231 | _refreshTokenSource.Cancel(); 232 | WaitForRefresh(); 233 | } 234 | 235 | if (disposing) 236 | { 237 | base.Dispose(); 238 | } 239 | } 240 | 241 | #endregion 242 | 243 | #region ICloneable Methods 244 | 245 | public override object Clone() 246 | { 247 | var clonedInternalWatcher = (IFileSystemWatcher) InternalWatcher.Clone(); 248 | return new FileSystemRefreshableWatcher(clonedInternalWatcher) { RefreshAttempInterval = this.RefreshAttempInterval }; 249 | } 250 | 251 | #endregion 252 | } 253 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Wrappers/FileSystemWatcherAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace FileSystemWatcherAlts.Wrappers 5 | { 6 | /// 7 | /// Adapts a FileSystemWatcher to make it fit the IFileSystemWatcher interface 8 | /// 9 | public class FileSystemWatcherAdapter : IFileSystemWatcher 10 | { 11 | #region Fields 12 | 13 | private FileSystemWatcher _watcher; 14 | 15 | #endregion 16 | 17 | #region Constructors 18 | 19 | public FileSystemWatcherAdapter(FileSystemWatcher watcherToWrap) 20 | { 21 | _watcher = watcherToWrap; 22 | SubscribeToPrivateWatcherEvents(); 23 | } 24 | public FileSystemWatcherAdapter() : this(new FileSystemWatcher()) 25 | { 26 | } 27 | 28 | public FileSystemWatcherAdapter(string path) : this(new FileSystemWatcher(path)) 29 | { 30 | } 31 | 32 | public FileSystemWatcherAdapter(string path, string filter) : this(new FileSystemWatcher(path,filter)) 33 | { 34 | } 35 | 36 | #endregion 37 | 38 | #region Events 39 | 40 | public event FileSystemEventHandler Changed; 41 | public event FileSystemEventHandler Created; 42 | public event FileSystemEventHandler Deleted; 43 | public event ErrorEventHandler Error; 44 | public event RenamedEventHandler Renamed; 45 | 46 | #endregion 47 | 48 | #region Proprties 49 | protected FileSystemWatcher InternalWatcher 50 | { 51 | get { return _watcher; } 52 | set 53 | { 54 | UnsubscribeFromPrivateWatcherEvents(); 55 | _watcher = value; 56 | SubscribeToPrivateWatcherEvents(); 57 | } 58 | } 59 | 60 | public bool EnableRaisingEvents 61 | { 62 | get 63 | { 64 | return InternalWatcher.EnableRaisingEvents; 65 | } 66 | set 67 | { 68 | InternalWatcher.EnableRaisingEvents = value; 69 | } 70 | } 71 | 72 | public string Filter 73 | { 74 | get 75 | { 76 | return InternalWatcher.Filter; 77 | } 78 | set 79 | { 80 | InternalWatcher.Filter = value; 81 | } 82 | } 83 | 84 | public bool IncludeSubdirectories 85 | { 86 | get 87 | { 88 | return InternalWatcher.IncludeSubdirectories; 89 | } 90 | set 91 | { 92 | InternalWatcher.IncludeSubdirectories = value; 93 | } 94 | } 95 | 96 | public int InternalBufferSize 97 | { 98 | get 99 | { 100 | return InternalWatcher.InternalBufferSize; 101 | } 102 | set 103 | { 104 | InternalWatcher.InternalBufferSize = value; 105 | } 106 | } 107 | 108 | public NotifyFilters NotifyFilter 109 | { 110 | get 111 | { 112 | return InternalWatcher.NotifyFilter; 113 | } 114 | set 115 | { 116 | InternalWatcher.NotifyFilter = value; 117 | } 118 | } 119 | 120 | public string Path 121 | { 122 | get 123 | { 124 | return InternalWatcher.Path; 125 | } 126 | set 127 | { 128 | InternalWatcher.Path = value; 129 | } 130 | } 131 | 132 | #endregion 133 | 134 | #region Watcher Refreshing 135 | 136 | protected void SubscribeToPrivateWatcherEvents() 137 | { 138 | if (InternalWatcher == null) return; 139 | 140 | InternalWatcher.Created += OnCreated; 141 | InternalWatcher.Changed += OnChanged; 142 | InternalWatcher.Deleted += OnDeleted; 143 | InternalWatcher.Error += OnError; 144 | InternalWatcher.Renamed += OnRenamed; 145 | } 146 | 147 | protected void UnsubscribeFromPrivateWatcherEvents() 148 | { 149 | if (InternalWatcher == null) return; 150 | 151 | InternalWatcher.Created -= OnCreated; 152 | InternalWatcher.Changed -= OnChanged; 153 | InternalWatcher.Deleted -= OnDeleted; 154 | InternalWatcher.Error -= OnError; 155 | InternalWatcher.Renamed -= OnRenamed; 156 | } 157 | 158 | protected void OnChanged(object sender, FileSystemEventArgs fileSystemEventArgs) => Changed?.Invoke(sender, fileSystemEventArgs); 159 | protected void OnCreated(object sender, FileSystemEventArgs fileSystemEventArgs) => Created?.Invoke(sender, fileSystemEventArgs); 160 | protected void OnDeleted(object sender, FileSystemEventArgs fileSystemEventArgs) => Deleted?.Invoke(sender, fileSystemEventArgs); 161 | protected void OnError(object sender, ErrorEventArgs fileSystemErrorArgs) => Error?.Invoke(sender, fileSystemErrorArgs); 162 | protected void OnRenamed(object sender, RenamedEventArgs fileSystemEventArgs) => Renamed?.Invoke(sender, fileSystemEventArgs); 163 | 164 | #endregion 165 | 166 | #region Mmethods 167 | 168 | public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) 169 | { 170 | return InternalWatcher.WaitForChanged(changeType); 171 | } 172 | 173 | public WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) 174 | { 175 | return InternalWatcher.WaitForChanged(changeType, timeout); 176 | } 177 | 178 | #endregion 179 | 180 | #region IDisposeable Methods 181 | 182 | ~FileSystemWatcherAdapter() 183 | { 184 | Dispose(false); 185 | } 186 | 187 | public void Dispose() 188 | { 189 | Dispose(true); 190 | GC.SuppressFinalize(this); 191 | } 192 | 193 | private void Dispose(bool disposing) 194 | { 195 | UnsubscribeFromPrivateWatcherEvents(); 196 | 197 | if (disposing) 198 | { 199 | _watcher.Dispose(); 200 | } 201 | } 202 | #endregion 203 | 204 | #region ICloneable Methods 205 | 206 | public object Clone() 207 | { 208 | FileSystemWatcher clonedEncapsWatcher = new FileSystemWatcher() 209 | { 210 | NotifyFilter = InternalWatcher.NotifyFilter, 211 | Path = InternalWatcher.Path, 212 | IncludeSubdirectories = InternalWatcher.IncludeSubdirectories, 213 | InternalBufferSize = InternalWatcher.InternalBufferSize, 214 | Filter = InternalWatcher.Filter, 215 | EnableRaisingEvents = InternalWatcher.EnableRaisingEvents 216 | }; 217 | return new FileSystemWatcherAdapter(clonedEncapsWatcher); 218 | } 219 | 220 | #endregion 221 | 222 | } 223 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/Wrappers/FileSystemWatcherWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace FileSystemWatcherAlts.Wrappers 5 | { 6 | /// 7 | /// An abstract wrapper for an IFilesystemWatcher 8 | /// 9 | public abstract class FileSystemWatcherWrapper : IFileSystemWatcher 10 | { 11 | 12 | #region Fields 13 | 14 | private IFileSystemWatcher _internalWatcher; 15 | 16 | #endregion 17 | 18 | #region Events 19 | 20 | public virtual event FileSystemEventHandler Changed; 21 | public virtual event FileSystemEventHandler Created; 22 | public virtual event FileSystemEventHandler Deleted; 23 | public virtual event ErrorEventHandler Error; 24 | public virtual event RenamedEventHandler Renamed; 25 | 26 | #endregion 27 | 28 | #region Proprties 29 | 30 | protected IFileSystemWatcher InternalWatcher 31 | { 32 | get { return _internalWatcher; } 33 | set 34 | { 35 | UnsubscribeFromInternalWatcherEvents(); 36 | _internalWatcher = value; 37 | SubscribeToPrivateWatcherEvents(); 38 | } 39 | } 40 | 41 | public virtual bool EnableRaisingEvents 42 | { 43 | get 44 | { 45 | return InternalWatcher.EnableRaisingEvents; 46 | } 47 | set 48 | { 49 | InternalWatcher.EnableRaisingEvents = value; 50 | } 51 | } 52 | 53 | public virtual string Filter 54 | { 55 | get 56 | { 57 | return InternalWatcher.Filter; 58 | } 59 | set 60 | { 61 | InternalWatcher.Filter = value; 62 | } 63 | } 64 | 65 | public virtual bool IncludeSubdirectories 66 | { 67 | get 68 | { 69 | return InternalWatcher.IncludeSubdirectories; 70 | } 71 | set 72 | { 73 | InternalWatcher.IncludeSubdirectories = value; 74 | } 75 | } 76 | 77 | public virtual int InternalBufferSize 78 | { 79 | get 80 | { 81 | return InternalWatcher.InternalBufferSize; 82 | } 83 | set 84 | { 85 | InternalWatcher.InternalBufferSize = value; 86 | } 87 | } 88 | 89 | public virtual NotifyFilters NotifyFilter 90 | { 91 | get 92 | { 93 | return InternalWatcher.NotifyFilter; 94 | } 95 | set 96 | { 97 | InternalWatcher.NotifyFilter = value; 98 | } 99 | } 100 | 101 | public virtual string Path 102 | { 103 | get 104 | { 105 | return InternalWatcher.Path; 106 | } 107 | set 108 | { 109 | InternalWatcher.Path = value; 110 | } 111 | } 112 | 113 | #endregion 114 | 115 | #region Constructors 116 | 117 | protected FileSystemWatcherWrapper(IFileSystemWatcher watcher) 118 | { 119 | InternalWatcher = watcher; 120 | } 121 | protected FileSystemWatcherWrapper(FileSystemWatcher watcher) : this(new FileSystemWatcherAdapter(watcher)) 122 | { 123 | } 124 | protected FileSystemWatcherWrapper() : this(new FileSystemWatcherAdapter()) 125 | { 126 | } 127 | protected FileSystemWatcherWrapper(string path) : this(new FileSystemWatcherAdapter(path)) 128 | { 129 | } 130 | protected FileSystemWatcherWrapper(string path, string filter) : this(new FileSystemWatcherAdapter(path, filter)) 131 | { 132 | } 133 | 134 | #endregion 135 | 136 | #region Events related Methods 137 | 138 | // Subscribe/Unsubscribe from wrapped watcher's events 139 | protected virtual void SubscribeToPrivateWatcherEvents() 140 | { 141 | if (InternalWatcher == null) return; 142 | 143 | InternalWatcher.Created += OnCreated; 144 | InternalWatcher.Changed += OnChanged; 145 | InternalWatcher.Deleted += OnDeleted; 146 | InternalWatcher.Error += OnError; 147 | InternalWatcher.Renamed += OnRenamed; 148 | } 149 | protected virtual void UnsubscribeFromInternalWatcherEvents() 150 | { 151 | if (InternalWatcher == null) return; 152 | 153 | InternalWatcher.Created -= OnCreated; 154 | InternalWatcher.Changed -= OnChanged; 155 | InternalWatcher.Deleted -= OnDeleted; 156 | InternalWatcher.Error -= OnError; 157 | InternalWatcher.Renamed -= OnRenamed; 158 | } 159 | 160 | // Events Invokers 161 | protected virtual void OnChanged(object sender, FileSystemEventArgs fileSystemEventArgs) => Changed?.Invoke(sender, fileSystemEventArgs); 162 | protected virtual void OnCreated(object sender, FileSystemEventArgs fileSystemEventArgs) => Created?.Invoke(sender, fileSystemEventArgs); 163 | protected virtual void OnDeleted(object sender, FileSystemEventArgs fileSystemEventArgs) => Deleted?.Invoke(sender, fileSystemEventArgs); 164 | protected virtual void OnError(object sender, ErrorEventArgs fileSystemErrorArgs) => Error?.Invoke(sender, fileSystemErrorArgs); 165 | protected virtual void OnRenamed(object sender, RenamedEventArgs fileSystemEventArgs) => Renamed?.Invoke(sender, fileSystemEventArgs); 166 | 167 | #endregion 168 | 169 | #region Override methods 170 | 171 | public virtual WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) 172 | { 173 | return InternalWatcher.WaitForChanged(changeType); 174 | } 175 | 176 | public virtual WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) 177 | { 178 | return InternalWatcher.WaitForChanged(changeType, timeout); 179 | } 180 | 181 | #endregion 182 | 183 | #region IDisposable Methods 184 | 185 | ~FileSystemWatcherWrapper() 186 | { 187 | Dispose(false); 188 | } 189 | 190 | public void Dispose() 191 | { 192 | Dispose(true); 193 | GC.SuppressFinalize(this); 194 | } 195 | 196 | private void Dispose(bool disposing) 197 | { 198 | UnsubscribeFromInternalWatcherEvents(); 199 | 200 | if (disposing) 201 | { 202 | InternalWatcher.Dispose(); 203 | } 204 | } 205 | 206 | #endregion 207 | 208 | #region ICloneable Methods 209 | 210 | public abstract object Clone(); 211 | 212 | #endregion 213 | 214 | } 215 | } -------------------------------------------------------------------------------- /FileSystemWatcherAlts/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Shai S 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ⚠️ This project is not maintained. 2 | I suggest seeking other solutions like this one: 3 | https://petermeinl.wordpress.com/2015/05/18/tamed-filesystemwatcher/ 4 | 5 | ![Icon](https://github.com/Wootness/FileSystemWatcherAlts/blob/master/FileSystemWatcherAlts/Icon/noun_160432_cc.png?raw=true) 6 | # FileSystemWatcherAlts[![Build status](https://ci.appveyor.com/api/projects/status/skjg78b5rr4xysam?svg=true)](https://ci.appveyor.com/project/Wootness/filesystemwatcheralts) 7 | 8 | ###### _Resolving your files watching trust issues since 2015._ 9 | 10 | 11 | ## What Is This? 12 | A collection of alternatives to the System.IO.FileSystemWatcher class. 13 | 14 | ## What's Wrong With System.IO.FileSystemWatcher? 15 | As you can see with a quick google search - FilesystemWatcher is far from perfect. 16 | In fact, it is problematic enough that Microsoft gave it an "Error" event so you can react when it encounters a problem. 17 | To put it in simple words, there are 2 main issues with it: 18 | 19 | 1. It misses file changes when under heavy load, rendering it unreliable. 20 | 2. It stops working under certain circumstances. 21 | 22 | ## Show Me What You Got 23 | The alternatives offered are: 24 | 25 | 1. `FileSystemPoller` - Periodicly polls for file system changes. 26 | 2. `FileSystemRefreshableWatcher` - A watcher wrapper. Allows the user to restart a broken watcher. 27 | 3. `FileSystemAutoRefreshingWatcher` - A watcher wrapper. *Automatically* restarts a watcher if it breaks. 28 | 4. `FileSystemOverseer` - A watcher wrapper. Automatically restarts a watcher if it breaks and uses a backup poller for increased reliability. 29 | 30 | The table over here shows you the upsides and downsides of FileSystemWatcher and my alternatives. 31 | Choose the one that fits your requirements. 32 | 33 | ## Usage 34 | 35 | All alternatives in this library implements an interface called **"IFileSystemWatcher"** 36 | It defines methods and events corresponding to the ones in System.IO.FileSystemWatcher. 37 | If your project is already utilizing FileSytemWatcher you can simply change this: 38 | 39 | ```C# 40 | FileSystemWatcher sysMonitor = new FileSystemWatcher(@"C:\"); 41 | ``` 42 | 43 | To any of those: 44 | 45 | ```C# 46 | IFileSystemWatcher sysMonitor = new FileSystemRefreshableWatcher(@"C:\"); 47 | IFileSystemWatcher sysMonitor = new FileSystemAutoRefreshingWatcher(@"C:\"); 48 | IFileSystemWatcher sysMonitor = new FileSystemPoller(pollingInterval: 500,path: @"C:\"); 49 | IFileSystemWatcher sysMonitor = new FileSystemOverseer(pollingInterval: 500, path: @"C:\"); 50 | ``` 51 | 52 | Then use Created/Deleted/Renamed/Changed events as you would with FileSystemWatcher. 53 | 54 | 55 | ## Thanks 56 | Icon: Binoculars designed by Gregor Crešnar from The Noun Project 57 | -------------------------------------------------------------------------------- /packages/Polly.2.2.3/Polly.2.2.3.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theXappy/FileSystemWatcherAlts/6e228ed78367cb7aca7e4aa90debb9d737584441/packages/Polly.2.2.3/Polly.2.2.3.nupkg -------------------------------------------------------------------------------- /packages/Polly.2.2.3/lib/net35/Polly.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theXappy/FileSystemWatcherAlts/6e228ed78367cb7aca7e4aa90debb9d737584441/packages/Polly.2.2.3/lib/net35/Polly.dll -------------------------------------------------------------------------------- /packages/Polly.2.2.3/lib/net35/Polly.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Polly 5 | 6 | 7 | 8 | 9 | Fluent API for defining a Circuit Breaker . 10 | 11 | 12 | 13 | 14 | Builds a that will function like a Circuit Breaker. 15 | The circuit will break after 16 | exceptions that are handled by this policy are raised. The circuit will stay 17 | broken for the . Any attempt to execute this policy 18 | while the circuit is broken, will immediately throw a containing the exception 19 | that broke the cicuit. 20 | 21 | If the first action after the break duration period results in an exception, the circuit will break 22 | again for another , otherwise it will reset. 23 | 24 | 25 | The policy builder. 26 | The number of exceptions that are allowed before opening the circuit. 27 | The duration the circuit will stay open before resetting. 28 | The policy instance. 29 | (see "Release It!" by Michael T. Nygard fi) 30 | exceptionsAllowedBeforeBreaking;Value must be greater than zero. 31 | 32 | 33 | 34 | Exception thrown when a circuit is broken. 35 | 36 | 37 | 38 | 39 | Initializes a new instance of the class. 40 | 41 | 42 | 43 | 44 | Initializes a new instance of the class. 45 | 46 | The message that describes the error. 47 | 48 | 49 | 50 | Initializes a new instance of the class. 51 | 52 | The message. 53 | The inner. 54 | 55 | 56 | 57 | Initializes a new instance of the class. 58 | 59 | The that holds the serialized object data about the exception being thrown. 60 | The that contains contextual information about the source or destination. 61 | 62 | 63 | 64 | A readonly dictionary of string key / object value pairs 65 | 66 | 67 | 68 | http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=29 69 | 70 | Provides the base class for a generic read-only dictionary. 71 | 72 | 73 | The type of keys in the dictionary. 74 | 75 | 76 | The type of values in the dictionary. 77 | 78 | 79 | 80 | An instance of the ReadOnlyDictionary generic class is 81 | always read-only. A dictionary that is read-only is simply a 82 | dictionary with a wrapper that prevents modifying the 83 | dictionary; therefore, if changes are made to the underlying 84 | dictionary, the read-only dictionary reflects those changes. 85 | See for a modifiable version of 86 | this class. 87 | 88 | 89 | Notes to Implementers This base class is provided to 90 | make it easier for implementers to create a generic read-only 91 | custom dictionary. Implementers are encouraged to extend this 92 | base class instead of creating their own. 93 | 94 | 95 | 96 | 97 | 98 | Initializes a new instance of the 99 | class that wraps 100 | the supplied . 101 | 102 | The 103 | that will be wrapped. 104 | 105 | Thrown when the dictionary is null. 106 | 107 | 108 | 109 | This method is not supported by the 110 | . 111 | 112 | The object to use as the key of the element to add. 113 | 114 | The object to use as the value of the element to add. 115 | 116 | 117 | Determines whether the 118 | contains the specified key. 119 | 120 | True if the contains 121 | an element with the specified key; otherwise, false. 122 | 123 | The key to locate in the 124 | . 125 | 126 | Thrown when the key is null. 127 | 128 | 129 | 130 | 131 | This method is not supported by the . 132 | 133 | The key of the element to remove. 134 | 135 | True if the element is successfully removed; otherwise, false. 136 | 137 | 138 | 139 | 140 | Gets the value associated with the specified key. 141 | 142 | The key of the value to get. 143 | When this method returns, contains the value 144 | associated with the specified key, if the key is found; 145 | otherwise, the default value for the type of the value parameter. 146 | This parameter is passed uninitialized. 147 | 148 | true if the contains 149 | an element with the specified key; otherwise, false. 150 | 151 | 152 | 153 | This method is not supported by the 154 | . 155 | 156 | The object to add to the . 157 | 158 | 159 | 160 | This method is not supported by the 161 | . 162 | 163 | 164 | 165 | Determines whether the contains a 166 | specific value. 167 | 168 | 169 | The object to locate in the . 170 | 171 | 172 | true if item is found in the ICollection; 173 | otherwise, false. 174 | 175 | 176 | 177 | 178 | Copies the elements of the ICollection to an Array, starting at a 179 | particular Array index. 180 | 181 | The one-dimensional Array that is the 182 | destination of the elements copied from ICollection. 183 | The Array must have zero-based indexing. 184 | 185 | 186 | The zero-based index in array at which copying begins. 187 | 188 | 189 | 190 | This method is not supported by the 191 | . 192 | 193 | The object to remove from the ICollection. 194 | 195 | Will never return a value. 196 | 197 | 198 | 199 | Returns an enumerator that iterates through the collection. 200 | 201 | 202 | A IEnumerator that can be used to iterate through the collection. 203 | 204 | 205 | 206 | 207 | Returns an enumerator that iterates through a collection. 208 | 209 | 210 | An IEnumerator that can be used to iterate through the collection. 211 | 212 | 213 | 214 | 215 | For a description of this member, see . 216 | 217 | 218 | The one-dimensional Array that is the destination of the elements copied from 219 | ICollection. The Array must have zero-based indexing. 220 | 221 | 222 | The zero-based index in Array at which copying begins. 223 | 224 | 225 | 226 | 227 | Gets the number of key/value pairs contained in the 228 | . 229 | 230 | The number of key/value pairs. 231 | The number of key/value pairs contained in the 232 | . 233 | 234 | 235 | Gets a collection containing the keys in the 236 | . 237 | A 238 | containing the keys. 239 | A 240 | 241 | containing the keys in the 242 | . 243 | 244 | 245 | 246 | 247 | Gets a collection containing the values of the 248 | . 249 | 250 | The collection of values. 251 | 252 | 253 | Gets a value indicating whether the dictionary is read-only. 254 | This value will always be true. 255 | 256 | 257 | 258 | Gets a value indicating whether access to the dictionary 259 | is synchronized (thread safe). 260 | 261 | 262 | 263 | 264 | Gets an object that can be used to synchronize access to dictionary. 265 | 266 | 267 | 268 | 269 | Gets or sets the value associated with the specified key. 270 | 271 | 272 | The value associated with the specified key. If the specified key 273 | is not found, a get operation throws a 274 | , 275 | and a set operation creates a new element with the specified key. 276 | 277 | The key of the value to get or set. 278 | 279 | Thrown when the key is null. 280 | 281 | 282 | The property is retrieved and key does not exist in the collection. 283 | 284 | 285 | 286 | 287 | Transient exception handling policies that can be applied to delegates. 288 | These policies can be called with arbitrary context data. 289 | 290 | 291 | 292 | 293 | Executes the specified action within the policy. 294 | 295 | The action to perform. 296 | Arbitrary data that is passed to the exception policy. 297 | contextData 298 | 299 | 300 | 301 | Executes the specified action within the policy. 302 | 303 | The action to perform. 304 | 305 | 306 | 307 | Executes the specified action within the policy and returns the Result. 308 | 309 | The type of the Result. 310 | The action to perform. 311 | Arbitrary data that is passed to the exception policy. 312 | 313 | The value returned by the action 314 | 315 | contextData 316 | 317 | 318 | 319 | Executes the specified action within the policy and returns the Result. 320 | 321 | The type of the Result. 322 | The action to perform. 323 | 324 | The value returned by the action 325 | 326 | 327 | 328 | 329 | Fluent API for chaining exceptions that will be handled by a . 330 | 331 | 332 | 333 | 334 | Specifies the type of exception that this policy can handle. 335 | 336 | The type of the exception to handle. 337 | The current builder to chain off. 338 | The PolicyBuilder instance. 339 | 340 | 341 | 342 | Specifies the type of exception that this policy can handle with addition filters on this exception type. 343 | 344 | The type of the exception. 345 | The current builder to chain off. 346 | The exception predicate to filter the type of exception this policy can handle. 347 | The PolicyBuilder instance. 348 | 349 | 350 | 351 | Transient exception handling policies that can 352 | be applied to delegates 353 | 354 | 355 | 356 | 357 | Executes the specified action within the policy. 358 | 359 | The action to perform. 360 | 361 | 362 | 363 | Executes the specified action within the policy and returns the captured result 364 | 365 | The action to perform. 366 | The captured result 367 | 368 | 369 | 370 | Executes the specified action within the policy and returns the result. 371 | 372 | The type of the result. 373 | The action to perform. 374 | The value returned by the action 375 | 376 | 377 | 378 | Executes the specified action within the policy and returns the captured result 379 | 380 | The action to perform. 381 | The captured result 382 | 383 | 384 | 385 | Specifies the type of exception that this policy can handle. 386 | 387 | The type of the exception to handle. 388 | The PolicyBuilder instance. 389 | 390 | 391 | 392 | Specifies the type of exception that this policy can handle with addition filters on this exception type. 393 | 394 | The type of the exception. 395 | The exception predicate to filter the type of exception this policy can handle. 396 | The PolicyBuilder instance. 397 | 398 | 399 | 400 | Builder class that holds the list of current exception predicates. 401 | 402 | 403 | 404 | 405 | Returns a that represents this instance. 406 | 407 | 408 | A that represents this instance. 409 | 410 | 411 | 412 | 413 | Determines whether the specified is equal to this instance. 414 | 415 | The to compare with this instance. 416 | 417 | true if the specified is equal to this instance; otherwise, false. 418 | 419 | 420 | 421 | 422 | Returns a hash code for this instance. 423 | 424 | 425 | A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 426 | 427 | 428 | 429 | 430 | Gets the of the current instance. 431 | 432 | 433 | The instance that represents the exact runtime type of the current instance. 434 | 435 | 436 | 437 | 438 | The captured result of executing a policy 439 | 440 | 441 | 442 | 443 | The outcome of executing the policy 444 | 445 | 446 | 447 | 448 | The final exception captured. Will be null if policy executed successfully 449 | 450 | 451 | 452 | 453 | The exception type of the final exception captured. Will be null if policy executed successfully 454 | 455 | 456 | 457 | 458 | The captured result of executing a policy 459 | 460 | 461 | 462 | 463 | The outcome of executing the policy 464 | 465 | 466 | 467 | 468 | The final exception captured. Will be null if policy executed successfully 469 | 470 | 471 | 472 | 473 | The exception type of the final exception captured. Will be null if policy executed successfully 474 | 475 | 476 | 477 | 478 | The result of executing the policy. Will be default(TResult) is the policy failed 479 | 480 | 481 | 482 | 483 | Represents the outcome of executing a policy 484 | 485 | 486 | 487 | 488 | Indicates that the policy ultimately executed successfully 489 | 490 | 491 | 492 | 493 | Indicates that the policy ultimately failed 494 | 495 | 496 | 497 | 498 | Represents the type of exception resulting from a failed policy 499 | 500 | 501 | 502 | 503 | An exception type that has been defined to be handled by this policy 504 | 505 | 506 | 507 | 508 | An exception type that has been not been defined to be handled by this policy 509 | 510 | 511 | 512 | 513 | Fluent API for defining a Retry . 514 | 515 | 516 | 517 | 518 | Builds a that will retry once. 519 | 520 | The policy builder. 521 | The policy instance. 522 | 523 | 524 | 525 | Builds a that will retry times. 526 | 527 | The policy builder. 528 | The retry count. 529 | The policy instance. 530 | 531 | 532 | 533 | Builds a that will retry once 534 | calling on retry with the raised exception and retry count. 535 | 536 | The policy builder. 537 | The action to call on each retry. 538 | The policy instance. 539 | retryCount;Value must be greater than zero. 540 | onRetry 541 | 542 | 543 | 544 | Builds a that will retry times 545 | calling on each retry with the raised exception and retry count. 546 | 547 | The policy builder. 548 | The retry count. 549 | The action to call on each retry. 550 | The policy instance. 551 | retryCount;Value must be greater than zero. 552 | onRetry 553 | 554 | 555 | 556 | Builds a that will retry once 557 | calling on retry with the raised exception, retry count and 558 | execution context. 559 | 560 | The policy builder. 561 | The action to call on each retry. 562 | The policy instance. 563 | retryCount;Value must be greater than zero. 564 | onRetry 565 | 566 | 567 | 568 | Builds a that will retry times 569 | calling on each retry with the raised exception, retry count and 570 | execution context. 571 | 572 | The policy builder. 573 | The retry count. 574 | The action to call on each retry. 575 | The policy instance. 576 | retryCount;Value must be greater than zero. 577 | onRetry 578 | 579 | 580 | 581 | Builds a that will retry indefinitely. 582 | 583 | The policy builder. 584 | The policy instance. 585 | 586 | 587 | 588 | Builds a that will retry indefinitely 589 | calling on each retry with the raised exception. 590 | 591 | The policy builder. 592 | The action to call on each retry. 593 | The policy instance. 594 | onRetry 595 | 596 | 597 | 598 | Builds a that will retry indefinitely 599 | calling on each retry with the raised exception and 600 | execution context. 601 | 602 | The policy builder. 603 | The action to call on each retry. 604 | The policy instance. 605 | onRetry 606 | 607 | 608 | 609 | Builds a that will wait and retry times. 610 | On each retry, the duration to wait is calculated by calling with 611 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 612 | 613 | The policy builder. 614 | The retry count. 615 | The function that provides the duration to wait for for a particular retry attempt. 616 | The policy instance. 617 | 618 | 619 | 620 | Builds a that will wait and retry times 621 | calling on each retry with the raised exception and the current sleep duration. 622 | On each retry, the duration to wait is calculated by calling with 623 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 624 | 625 | The policy builder. 626 | The retry count. 627 | The function that provides the duration to wait for for a particular retry attempt. 628 | The action to call on each retry. 629 | The policy instance. 630 | retryCount;Value must be greater than zero. 631 | 632 | timeSpanProvider 633 | or 634 | onRetry 635 | 636 | 637 | 638 | 639 | Builds a that will wait and retry times 640 | calling on each retry with the raised exception, current sleep duration and 641 | execution context. 642 | On each retry, the duration to wait is calculated by calling with 643 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 644 | 645 | The policy builder. 646 | The retry count. 647 | The function that provides the duration to wait for for a particular retry attempt. 648 | The action to call on each retry. 649 | The policy instance. 650 | retryCount;Value must be greater than zero. 651 | 652 | timeSpanProvider 653 | or 654 | onRetry 655 | 656 | 657 | 658 | 659 | Builds a that will wait and retry as many times as there are provided 660 | On each retry, the duration to wait is the current item. 661 | 662 | The policy builder. 663 | The sleep durations to wait for on each retry. 664 | The policy instance. 665 | 666 | 667 | 668 | Builds a that will wait and retry as many times as there are provided 669 | calling on each retry with the raised exception and the current sleep duration. 670 | On each retry, the duration to wait is the current item. 671 | 672 | The policy builder. 673 | The sleep durations to wait for on each retry. 674 | The action to call on each retry. 675 | The policy instance. 676 | 677 | sleepDurations 678 | or 679 | onRetry 680 | 681 | 682 | 683 | 684 | Builds a that will wait and retry as many times as there are provided 685 | calling on each retry with the raised exception, current sleep duration and 686 | execution context. 687 | On each retry, the duration to wait is the current item. 688 | 689 | The policy builder. 690 | The sleep durations to wait for on each retry. 691 | The action to call on each retry. 692 | The policy instance. 693 | 694 | sleepDurations 695 | or 696 | onRetry 697 | 698 | 699 | 700 | 701 | Time related delegates used to improve testability of the code 702 | 703 | 704 | 705 | 706 | Allows the setting of a custom Thread.Sleep implementation for testing. 707 | By default this will be a call to 708 | 709 | 710 | 711 | 712 | Allows the setting of a custom DateTime.UtcNow implementation for testing. 713 | By default this will be a call to 714 | 715 | 716 | 717 | 718 | Resets the custom implementations to their defaults. 719 | Should be called during test teardowns. 720 | 721 | 722 | 723 | 724 | -------------------------------------------------------------------------------- /packages/Polly.2.2.3/lib/net40/Polly.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theXappy/FileSystemWatcherAlts/6e228ed78367cb7aca7e4aa90debb9d737584441/packages/Polly.2.2.3/lib/net40/Polly.dll -------------------------------------------------------------------------------- /packages/Polly.2.2.3/lib/net45/Polly.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theXappy/FileSystemWatcherAlts/6e228ed78367cb7aca7e4aa90debb9d737584441/packages/Polly.2.2.3/lib/net45/Polly.dll -------------------------------------------------------------------------------- /packages/Polly.2.2.3/lib/net45/Polly.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Polly 5 | 6 | 7 | 8 | 9 | Fluent API for defining a Circuit Breaker . 10 | 11 | 12 | 13 | 14 | Builds a that will function like a Circuit Breaker. 15 | The circuit will break after 16 | exceptions that are handled by this policy are raised. The circuit will stay 17 | broken for the . Any attempt to execute this policy 18 | while the circuit is broken, will immediately throw a containing the exception 19 | that broke the cicuit. 20 | 21 | If the first action after the break duration period results in an exception, the circuit will break 22 | again for another , otherwise it will reset. 23 | 24 | 25 | The policy builder. 26 | The number of exceptions that are allowed before opening the circuit. 27 | The duration the circuit will stay open before resetting. 28 | The policy instance. 29 | (see "Release It!" by Michael T. Nygard fi) 30 | exceptionsAllowedBeforeBreaking;Value must be greater than zero. 31 | 32 | 33 | 34 | Exception thrown when a circuit is broken. 35 | 36 | 37 | 38 | 39 | Initializes a new instance of the class. 40 | 41 | 42 | 43 | 44 | Initializes a new instance of the class. 45 | 46 | The message that describes the error. 47 | 48 | 49 | 50 | Initializes a new instance of the class. 51 | 52 | The message. 53 | The inner. 54 | 55 | 56 | 57 | Initializes a new instance of the class. 58 | 59 | The that holds the serialized object data about the exception being thrown. 60 | The that contains contextual information about the source or destination. 61 | 62 | 63 | 64 | Transient exception handling policies that can be applied to delegates. 65 | These policies can be called with arbitrary context data. 66 | 67 | 68 | 69 | 70 | Executes the specified action within the policy. 71 | 72 | The action to perform. 73 | Arbitrary data that is passed to the exception policy. 74 | contextData 75 | 76 | 77 | 78 | Executes the specified action within the policy. 79 | 80 | The action to perform. 81 | 82 | 83 | 84 | Executes the specified action within the policy and returns the Result. 85 | 86 | The type of the Result. 87 | The action to perform. 88 | Arbitrary data that is passed to the exception policy. 89 | 90 | The value returned by the action 91 | 92 | contextData 93 | 94 | 95 | 96 | Executes the specified action within the policy and returns the Result. 97 | 98 | The type of the Result. 99 | The action to perform. 100 | 101 | The value returned by the action 102 | 103 | 104 | 105 | 106 | Fluent API for chaining exceptions that will be handled by a . 107 | 108 | 109 | 110 | 111 | Specifies the type of exception that this policy can handle. 112 | 113 | The type of the exception to handle. 114 | The current builder to chain off. 115 | The PolicyBuilder instance. 116 | 117 | 118 | 119 | Specifies the type of exception that this policy can handle with addition filters on this exception type. 120 | 121 | The type of the exception. 122 | The current builder to chain off. 123 | The exception predicate to filter the type of exception this policy can handle. 124 | The PolicyBuilder instance. 125 | 126 | 127 | 128 | Transient exception handling policies that can 129 | be applied to delegates 130 | 131 | 132 | 133 | 134 | Executes the specified action within the policy. 135 | 136 | The action to perform. 137 | 138 | 139 | 140 | Executes the specified action within the policy and returns the captured result 141 | 142 | The action to perform. 143 | The captured result 144 | 145 | 146 | 147 | Executes the specified action within the policy and returns the result. 148 | 149 | The type of the result. 150 | The action to perform. 151 | The value returned by the action 152 | 153 | 154 | 155 | Executes the specified action within the policy and returns the captured result 156 | 157 | The action to perform. 158 | The captured result 159 | 160 | 161 | 162 | Specifies the type of exception that this policy can handle. 163 | 164 | The type of the exception to handle. 165 | The PolicyBuilder instance. 166 | 167 | 168 | 169 | Specifies the type of exception that this policy can handle with addition filters on this exception type. 170 | 171 | The type of the exception. 172 | The exception predicate to filter the type of exception this policy can handle. 173 | The PolicyBuilder instance. 174 | 175 | 176 | 177 | Executes the specified asynchronous action within the policy. 178 | 179 | The action to perform. 180 | 181 | 182 | 183 | Executes the specified asynchronous action within the policy and returns the captured result. 184 | 185 | The action to perform. 186 | 187 | 188 | 189 | Executes the specified asynchronous action within the policy and returns the result. 190 | 191 | The type of the result. 192 | The action to perform. 193 | The value returned by the action 194 | 195 | 196 | 197 | Executes the specified asynchronous action within the policy and returns the result. 198 | 199 | The type of the result. 200 | The action to perform. 201 | The value returned by the action 202 | 203 | 204 | 205 | Builder class that holds the list of current exception predicates. 206 | 207 | 208 | 209 | 210 | Returns a that represents this instance. 211 | 212 | 213 | A that represents this instance. 214 | 215 | 216 | 217 | 218 | Determines whether the specified is equal to this instance. 219 | 220 | The to compare with this instance. 221 | 222 | true if the specified is equal to this instance; otherwise, false. 223 | 224 | 225 | 226 | 227 | Returns a hash code for this instance. 228 | 229 | 230 | A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 231 | 232 | 233 | 234 | 235 | Gets the of the current instance. 236 | 237 | 238 | The instance that represents the exact runtime type of the current instance. 239 | 240 | 241 | 242 | 243 | The captured result of executing a policy 244 | 245 | 246 | 247 | 248 | The outcome of executing the policy 249 | 250 | 251 | 252 | 253 | The final exception captured. Will be null if policy executed successfully 254 | 255 | 256 | 257 | 258 | The exception type of the final exception captured. Will be null if policy executed successfully 259 | 260 | 261 | 262 | 263 | The captured result of executing a policy 264 | 265 | 266 | 267 | 268 | The outcome of executing the policy 269 | 270 | 271 | 272 | 273 | The final exception captured. Will be null if policy executed successfully 274 | 275 | 276 | 277 | 278 | The exception type of the final exception captured. Will be null if policy executed successfully 279 | 280 | 281 | 282 | 283 | The result of executing the policy. Will be default(TResult) is the policy failed 284 | 285 | 286 | 287 | 288 | Represents the outcome of executing a policy 289 | 290 | 291 | 292 | 293 | Indicates that the policy ultimately executed successfully 294 | 295 | 296 | 297 | 298 | Indicates that the policy ultimately failed 299 | 300 | 301 | 302 | 303 | Represents the type of exception resulting from a failed policy 304 | 305 | 306 | 307 | 308 | An exception type that has been defined to be handled by this policy 309 | 310 | 311 | 312 | 313 | An exception type that has been not been defined to be handled by this policy 314 | 315 | 316 | 317 | 318 | Fluent API for defining a Retry . 319 | 320 | 321 | Fluent API for defining a Retry . 322 | 323 | 324 | 325 | 326 | Builds a that will retry once. 327 | 328 | The policy builder. 329 | The policy instance. 330 | 331 | 332 | 333 | Builds a that will retry times. 334 | 335 | The policy builder. 336 | The retry count. 337 | The policy instance. 338 | 339 | 340 | 341 | Builds a that will retry once 342 | calling on retry with the raised exception and retry count. 343 | 344 | The policy builder. 345 | The action to call on each retry. 346 | The policy instance. 347 | retryCount;Value must be greater than zero. 348 | onRetry 349 | 350 | 351 | 352 | Builds a that will retry times 353 | calling on each retry with the raised exception and retry count. 354 | 355 | The policy builder. 356 | The retry count. 357 | The action to call on each retry. 358 | The policy instance. 359 | retryCount;Value must be greater than zero. 360 | onRetry 361 | 362 | 363 | 364 | Builds a that will retry once 365 | calling on retry with the raised exception, retry count and 366 | execution context. 367 | 368 | The policy builder. 369 | The action to call on each retry. 370 | The policy instance. 371 | retryCount;Value must be greater than zero. 372 | onRetry 373 | 374 | 375 | 376 | Builds a that will retry times 377 | calling on each retry with the raised exception, retry count and 378 | execution context. 379 | 380 | The policy builder. 381 | The retry count. 382 | The action to call on each retry. 383 | The policy instance. 384 | retryCount;Value must be greater than zero. 385 | onRetry 386 | 387 | 388 | 389 | Builds a that will retry indefinitely. 390 | 391 | The policy builder. 392 | The policy instance. 393 | 394 | 395 | 396 | Builds a that will retry indefinitely 397 | calling on each retry with the raised exception. 398 | 399 | The policy builder. 400 | The action to call on each retry. 401 | The policy instance. 402 | onRetry 403 | 404 | 405 | 406 | Builds a that will retry indefinitely 407 | calling on each retry with the raised exception and 408 | execution context. 409 | 410 | The policy builder. 411 | The action to call on each retry. 412 | The policy instance. 413 | onRetry 414 | 415 | 416 | 417 | Builds a that will wait and retry times. 418 | On each retry, the duration to wait is calculated by calling with 419 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 420 | 421 | The policy builder. 422 | The retry count. 423 | The function that provides the duration to wait for for a particular retry attempt. 424 | The policy instance. 425 | 426 | 427 | 428 | Builds a that will wait and retry times 429 | calling on each retry with the raised exception and the current sleep duration. 430 | On each retry, the duration to wait is calculated by calling with 431 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 432 | 433 | The policy builder. 434 | The retry count. 435 | The function that provides the duration to wait for for a particular retry attempt. 436 | The action to call on each retry. 437 | The policy instance. 438 | retryCount;Value must be greater than zero. 439 | 440 | timeSpanProvider 441 | or 442 | onRetry 443 | 444 | 445 | 446 | 447 | Builds a that will wait and retry times 448 | calling on each retry with the raised exception, current sleep duration and 449 | execution context. 450 | On each retry, the duration to wait is calculated by calling with 451 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 452 | 453 | The policy builder. 454 | The retry count. 455 | The function that provides the duration to wait for for a particular retry attempt. 456 | The action to call on each retry. 457 | The policy instance. 458 | retryCount;Value must be greater than zero. 459 | 460 | timeSpanProvider 461 | or 462 | onRetry 463 | 464 | 465 | 466 | 467 | Builds a that will wait and retry as many times as there are provided 468 | On each retry, the duration to wait is the current item. 469 | 470 | The policy builder. 471 | The sleep durations to wait for on each retry. 472 | The policy instance. 473 | 474 | 475 | 476 | Builds a that will wait and retry as many times as there are provided 477 | calling on each retry with the raised exception and the current sleep duration. 478 | On each retry, the duration to wait is the current item. 479 | 480 | The policy builder. 481 | The sleep durations to wait for on each retry. 482 | The action to call on each retry. 483 | The policy instance. 484 | 485 | sleepDurations 486 | or 487 | onRetry 488 | 489 | 490 | 491 | 492 | Builds a that will wait and retry as many times as there are provided 493 | calling on each retry with the raised exception, current sleep duration and 494 | execution context. 495 | On each retry, the duration to wait is the current item. 496 | 497 | The policy builder. 498 | The sleep durations to wait for on each retry. 499 | The action to call on each retry. 500 | The policy instance. 501 | 502 | sleepDurations 503 | or 504 | onRetry 505 | 506 | 507 | 508 | 509 | Builds a that will retry once. 510 | 511 | The policy builder. 512 | The policy instance. 513 | 514 | 515 | 516 | Builds a that will retry times. 517 | 518 | The policy builder. 519 | The retry count. 520 | The policy instance. 521 | 522 | 523 | 524 | Builds a that will retry once 525 | calling on retry with the raised exception and retry count. 526 | 527 | The policy builder. 528 | The action to call on each retry. 529 | The policy instance. 530 | retryCount;Value must be greater than zero. 531 | onRetry 532 | 533 | 534 | 535 | Builds a that will retry times 536 | calling on each retry with the raised exception and retry count. 537 | 538 | The policy builder. 539 | The retry count. 540 | The action to call on each retry. 541 | The policy instance. 542 | retryCount;Value must be greater than zero. 543 | onRetry 544 | 545 | 546 | 547 | Builds a that will retry indefinitely. 548 | 549 | The policy builder. 550 | The policy instance. 551 | 552 | 553 | 554 | Builds a that will retry indefinitely 555 | calling on each retry with the raised exception. 556 | 557 | The policy builder. 558 | The action to call on each retry. 559 | The policy instance. 560 | onRetry 561 | 562 | 563 | 564 | Builds a that will wait and retry as many times as there are provided 565 | 566 | On each retry, the duration to wait is the current item. 567 | 568 | The policy builder. 569 | The sleep durations to wait for on each retry. 570 | The policy instance. 571 | 572 | 573 | 574 | Builds a that will wait and retry times. 575 | On each retry, the duration to wait is calculated by calling with 576 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 577 | 578 | The policy builder. 579 | The retry count. 580 | The function that provides the duration to wait for for a particular retry attempt. 581 | The policy instance. 582 | 583 | 584 | 585 | Builds a that will wait and retry times 586 | calling on each retry with the raised exception and the current sleep duration. 587 | On each retry, the duration to wait is calculated by calling with 588 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 589 | 590 | The policy builder. 591 | The retry count. 592 | The function that provides the duration to wait for for a particular retry attempt. 593 | The action to call on each retry. 594 | The policy instance. 595 | retryCount;Value must be greater than zero. 596 | 597 | timeSpanProvider 598 | or 599 | onRetry 600 | 601 | 602 | 603 | 604 | Builds a that will wait and retry as many times as there are provided 605 | 606 | calling on each retry with the raised exception and the current sleep duration. 607 | On each retry, the duration to wait is the current item. 608 | 609 | The policy builder. 610 | The sleep durations to wait for on each retry. 611 | The action to call on each retry. 612 | The policy instance. 613 | 614 | sleepDurations 615 | or 616 | onRetry 617 | 618 | 619 | 620 | 621 | Time related delegates used to improve testability of the code 622 | 623 | 624 | 625 | 626 | Allows the setting of a custom Thread.Sleep implementation for testing. 627 | By default this will be a call to 628 | 629 | 630 | 631 | 632 | Allows the setting of a custom DateTime.UtcNow implementation for testing. 633 | By default this will be a call to 634 | 635 | 636 | 637 | 638 | Resets the custom implementations to their defaults. 639 | Should be called during test teardowns. 640 | 641 | 642 | 643 | 644 | Fluent API for defining a Circuit Breaker . 645 | 646 | 647 | 648 | 649 | Builds a that will function like a Circuit Breaker. 650 | The circuit will break after 651 | exceptions that are handled by this policy are raised. The circuit will stay 652 | broken for the . Any attempt to execute this policy 653 | while the circuit is broken, will immediately throw a containing the exception 654 | that broke the cicuit. 655 | 656 | If the first action after the break duration period results in an exception, the circuit will break 657 | again for another , otherwise it will reset. 658 | 659 | 660 | The policy builder. 661 | The number of exceptions that are allowed before opening the circuit. 662 | The duration the circuit will stay open before resetting. 663 | The policy instance. 664 | (see "Release It!" by Michael T. Nygard fi) 665 | exceptionsAllowedBeforeBreaking;Value must be greater than zero. 666 | 667 | 668 | 669 | A readonly dictionary of string key / object value pairs 670 | 671 | 672 | 673 | 674 | -------------------------------------------------------------------------------- /packages/Polly.2.2.3/lib/portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1/Polly.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theXappy/FileSystemWatcherAlts/6e228ed78367cb7aca7e4aa90debb9d737584441/packages/Polly.2.2.3/lib/portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1/Polly.dll -------------------------------------------------------------------------------- /packages/Polly.2.2.3/lib/portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1/Polly.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Polly 5 | 6 | 7 | 8 | 9 | Fluent API for defining a Circuit Breaker . 10 | 11 | 12 | 13 | 14 | Builds a that will function like a Circuit Breaker. 15 | The circuit will break after 16 | exceptions that are handled by this policy are raised. The circuit will stay 17 | broken for the . Any attempt to execute this policy 18 | while the circuit is broken, will immediately throw a containing the exception 19 | that broke the cicuit. 20 | 21 | If the first action after the break duration period results in an exception, the circuit will break 22 | again for another , otherwise it will reset. 23 | 24 | 25 | The policy builder. 26 | The number of exceptions that are allowed before opening the circuit. 27 | The duration the circuit will stay open before resetting. 28 | The policy instance. 29 | (see "Release It!" by Michael T. Nygard fi) 30 | exceptionsAllowedBeforeBreaking;Value must be greater than zero. 31 | 32 | 33 | 34 | Exception thrown when a circuit is broken. 35 | 36 | 37 | 38 | 39 | Initializes a new instance of the class. 40 | 41 | 42 | 43 | 44 | Initializes a new instance of the class. 45 | 46 | The message that describes the error. 47 | 48 | 49 | 50 | Initializes a new instance of the class. 51 | 52 | The message. 53 | The inner. 54 | 55 | 56 | 57 | Transient exception handling policies that can be applied to delegates. 58 | These policies can be called with arbitrary context data. 59 | 60 | 61 | 62 | 63 | Executes the specified action within the policy. 64 | 65 | The action to perform. 66 | Arbitrary data that is passed to the exception policy. 67 | contextData 68 | 69 | 70 | 71 | Executes the specified action within the policy. 72 | 73 | The action to perform. 74 | 75 | 76 | 77 | Executes the specified action within the policy and returns the Result. 78 | 79 | The type of the Result. 80 | The action to perform. 81 | Arbitrary data that is passed to the exception policy. 82 | 83 | The value returned by the action 84 | 85 | contextData 86 | 87 | 88 | 89 | Executes the specified action within the policy and returns the Result. 90 | 91 | The type of the Result. 92 | The action to perform. 93 | 94 | The value returned by the action 95 | 96 | 97 | 98 | 99 | Fluent API for chaining exceptions that will be handled by a . 100 | 101 | 102 | 103 | 104 | Specifies the type of exception that this policy can handle. 105 | 106 | The type of the exception to handle. 107 | The current builder to chain off. 108 | The PolicyBuilder instance. 109 | 110 | 111 | 112 | Specifies the type of exception that this policy can handle with addition filters on this exception type. 113 | 114 | The type of the exception. 115 | The current builder to chain off. 116 | The exception predicate to filter the type of exception this policy can handle. 117 | The PolicyBuilder instance. 118 | 119 | 120 | 121 | Transient exception handling policies that can 122 | be applied to delegates 123 | 124 | 125 | 126 | 127 | Executes the specified action within the policy. 128 | 129 | The action to perform. 130 | 131 | 132 | 133 | Executes the specified action within the policy and returns the captured result 134 | 135 | The action to perform. 136 | The captured result 137 | 138 | 139 | 140 | Executes the specified action within the policy and returns the result. 141 | 142 | The type of the result. 143 | The action to perform. 144 | The value returned by the action 145 | 146 | 147 | 148 | Executes the specified action within the policy and returns the captured result 149 | 150 | The action to perform. 151 | The captured result 152 | 153 | 154 | 155 | Specifies the type of exception that this policy can handle. 156 | 157 | The type of the exception to handle. 158 | The PolicyBuilder instance. 159 | 160 | 161 | 162 | Specifies the type of exception that this policy can handle with addition filters on this exception type. 163 | 164 | The type of the exception. 165 | The exception predicate to filter the type of exception this policy can handle. 166 | The PolicyBuilder instance. 167 | 168 | 169 | 170 | Executes the specified asynchronous action within the policy. 171 | 172 | The action to perform. 173 | 174 | 175 | 176 | Executes the specified asynchronous action within the policy and returns the captured result. 177 | 178 | The action to perform. 179 | 180 | 181 | 182 | Executes the specified asynchronous action within the policy and returns the result. 183 | 184 | The type of the result. 185 | The action to perform. 186 | The value returned by the action 187 | 188 | 189 | 190 | Executes the specified asynchronous action within the policy and returns the result. 191 | 192 | The type of the result. 193 | The action to perform. 194 | The value returned by the action 195 | 196 | 197 | 198 | Builder class that holds the list of current exception predicates. 199 | 200 | 201 | 202 | 203 | Returns a that represents this instance. 204 | 205 | 206 | A that represents this instance. 207 | 208 | 209 | 210 | 211 | Determines whether the specified is equal to this instance. 212 | 213 | The to compare with this instance. 214 | 215 | true if the specified is equal to this instance; otherwise, false. 216 | 217 | 218 | 219 | 220 | Returns a hash code for this instance. 221 | 222 | 223 | A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 224 | 225 | 226 | 227 | 228 | Gets the of the current instance. 229 | 230 | 231 | The instance that represents the exact runtime type of the current instance. 232 | 233 | 234 | 235 | 236 | The captured result of executing a policy 237 | 238 | 239 | 240 | 241 | The outcome of executing the policy 242 | 243 | 244 | 245 | 246 | The final exception captured. Will be null if policy executed successfully 247 | 248 | 249 | 250 | 251 | The exception type of the final exception captured. Will be null if policy executed successfully 252 | 253 | 254 | 255 | 256 | The captured result of executing a policy 257 | 258 | 259 | 260 | 261 | The outcome of executing the policy 262 | 263 | 264 | 265 | 266 | The final exception captured. Will be null if policy executed successfully 267 | 268 | 269 | 270 | 271 | The exception type of the final exception captured. Will be null if policy executed successfully 272 | 273 | 274 | 275 | 276 | The result of executing the policy. Will be default(TResult) is the policy failed 277 | 278 | 279 | 280 | 281 | Represents the outcome of executing a policy 282 | 283 | 284 | 285 | 286 | Indicates that the policy ultimately executed successfully 287 | 288 | 289 | 290 | 291 | Indicates that the policy ultimately failed 292 | 293 | 294 | 295 | 296 | Represents the type of exception resulting from a failed policy 297 | 298 | 299 | 300 | 301 | An exception type that has been defined to be handled by this policy 302 | 303 | 304 | 305 | 306 | An exception type that has been not been defined to be handled by this policy 307 | 308 | 309 | 310 | 311 | Fluent API for defining a Retry . 312 | 313 | 314 | Fluent API for defining a Retry . 315 | 316 | 317 | 318 | 319 | Builds a that will retry once. 320 | 321 | The policy builder. 322 | The policy instance. 323 | 324 | 325 | 326 | Builds a that will retry times. 327 | 328 | The policy builder. 329 | The retry count. 330 | The policy instance. 331 | 332 | 333 | 334 | Builds a that will retry once 335 | calling on retry with the raised exception and retry count. 336 | 337 | The policy builder. 338 | The action to call on each retry. 339 | The policy instance. 340 | retryCount;Value must be greater than zero. 341 | onRetry 342 | 343 | 344 | 345 | Builds a that will retry times 346 | calling on each retry with the raised exception and retry count. 347 | 348 | The policy builder. 349 | The retry count. 350 | The action to call on each retry. 351 | The policy instance. 352 | retryCount;Value must be greater than zero. 353 | onRetry 354 | 355 | 356 | 357 | Builds a that will retry once 358 | calling on retry with the raised exception, retry count and 359 | execution context. 360 | 361 | The policy builder. 362 | The action to call on each retry. 363 | The policy instance. 364 | retryCount;Value must be greater than zero. 365 | onRetry 366 | 367 | 368 | 369 | Builds a that will retry times 370 | calling on each retry with the raised exception, retry count and 371 | execution context. 372 | 373 | The policy builder. 374 | The retry count. 375 | The action to call on each retry. 376 | The policy instance. 377 | retryCount;Value must be greater than zero. 378 | onRetry 379 | 380 | 381 | 382 | Builds a that will retry indefinitely. 383 | 384 | The policy builder. 385 | The policy instance. 386 | 387 | 388 | 389 | Builds a that will retry indefinitely 390 | calling on each retry with the raised exception. 391 | 392 | The policy builder. 393 | The action to call on each retry. 394 | The policy instance. 395 | onRetry 396 | 397 | 398 | 399 | Builds a that will retry indefinitely 400 | calling on each retry with the raised exception and 401 | execution context. 402 | 403 | The policy builder. 404 | The action to call on each retry. 405 | The policy instance. 406 | onRetry 407 | 408 | 409 | 410 | Builds a that will wait and retry times. 411 | On each retry, the duration to wait is calculated by calling with 412 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 413 | 414 | The policy builder. 415 | The retry count. 416 | The function that provides the duration to wait for for a particular retry attempt. 417 | The policy instance. 418 | 419 | 420 | 421 | Builds a that will wait and retry times 422 | calling on each retry with the raised exception and the current sleep duration. 423 | On each retry, the duration to wait is calculated by calling with 424 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 425 | 426 | The policy builder. 427 | The retry count. 428 | The function that provides the duration to wait for for a particular retry attempt. 429 | The action to call on each retry. 430 | The policy instance. 431 | retryCount;Value must be greater than zero. 432 | 433 | timeSpanProvider 434 | or 435 | onRetry 436 | 437 | 438 | 439 | 440 | Builds a that will wait and retry times 441 | calling on each retry with the raised exception, current sleep duration and 442 | execution context. 443 | On each retry, the duration to wait is calculated by calling with 444 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 445 | 446 | The policy builder. 447 | The retry count. 448 | The function that provides the duration to wait for for a particular retry attempt. 449 | The action to call on each retry. 450 | The policy instance. 451 | retryCount;Value must be greater than zero. 452 | 453 | timeSpanProvider 454 | or 455 | onRetry 456 | 457 | 458 | 459 | 460 | Builds a that will wait and retry as many times as there are provided 461 | On each retry, the duration to wait is the current item. 462 | 463 | The policy builder. 464 | The sleep durations to wait for on each retry. 465 | The policy instance. 466 | 467 | 468 | 469 | Builds a that will wait and retry as many times as there are provided 470 | calling on each retry with the raised exception and the current sleep duration. 471 | On each retry, the duration to wait is the current item. 472 | 473 | The policy builder. 474 | The sleep durations to wait for on each retry. 475 | The action to call on each retry. 476 | The policy instance. 477 | 478 | sleepDurations 479 | or 480 | onRetry 481 | 482 | 483 | 484 | 485 | Builds a that will wait and retry as many times as there are provided 486 | calling on each retry with the raised exception, current sleep duration and 487 | execution context. 488 | On each retry, the duration to wait is the current item. 489 | 490 | The policy builder. 491 | The sleep durations to wait for on each retry. 492 | The action to call on each retry. 493 | The policy instance. 494 | 495 | sleepDurations 496 | or 497 | onRetry 498 | 499 | 500 | 501 | 502 | Builds a that will retry once. 503 | 504 | The policy builder. 505 | The policy instance. 506 | 507 | 508 | 509 | Builds a that will retry times. 510 | 511 | The policy builder. 512 | The retry count. 513 | The policy instance. 514 | 515 | 516 | 517 | Builds a that will retry once 518 | calling on retry with the raised exception and retry count. 519 | 520 | The policy builder. 521 | The action to call on each retry. 522 | The policy instance. 523 | retryCount;Value must be greater than zero. 524 | onRetry 525 | 526 | 527 | 528 | Builds a that will retry times 529 | calling on each retry with the raised exception and retry count. 530 | 531 | The policy builder. 532 | The retry count. 533 | The action to call on each retry. 534 | The policy instance. 535 | retryCount;Value must be greater than zero. 536 | onRetry 537 | 538 | 539 | 540 | Builds a that will retry indefinitely. 541 | 542 | The policy builder. 543 | The policy instance. 544 | 545 | 546 | 547 | Builds a that will retry indefinitely 548 | calling on each retry with the raised exception. 549 | 550 | The policy builder. 551 | The action to call on each retry. 552 | The policy instance. 553 | onRetry 554 | 555 | 556 | 557 | Builds a that will wait and retry as many times as there are provided 558 | 559 | On each retry, the duration to wait is the current item. 560 | 561 | The policy builder. 562 | The sleep durations to wait for on each retry. 563 | The policy instance. 564 | 565 | 566 | 567 | Builds a that will wait and retry times. 568 | On each retry, the duration to wait is calculated by calling with 569 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 570 | 571 | The policy builder. 572 | The retry count. 573 | The function that provides the duration to wait for for a particular retry attempt. 574 | The policy instance. 575 | 576 | 577 | 578 | Builds a that will wait and retry times 579 | calling on each retry with the raised exception and the current sleep duration. 580 | On each retry, the duration to wait is calculated by calling with 581 | the current retry attempt allowing an exponentially increasing wait time (exponential backoff). 582 | 583 | The policy builder. 584 | The retry count. 585 | The function that provides the duration to wait for for a particular retry attempt. 586 | The action to call on each retry. 587 | The policy instance. 588 | retryCount;Value must be greater than zero. 589 | 590 | timeSpanProvider 591 | or 592 | onRetry 593 | 594 | 595 | 596 | 597 | Builds a that will wait and retry as many times as there are provided 598 | 599 | calling on each retry with the raised exception and the current sleep duration. 600 | On each retry, the duration to wait is the current item. 601 | 602 | The policy builder. 603 | The sleep durations to wait for on each retry. 604 | The action to call on each retry. 605 | The policy instance. 606 | 607 | sleepDurations 608 | or 609 | onRetry 610 | 611 | 612 | 613 | 614 | Time related delegates used to improve testability of the code 615 | 616 | 617 | 618 | 619 | Allows the setting of a custom Thread.Sleep implementation for testing. 620 | By default this will be a call to 621 | 622 | 623 | 624 | 625 | Allows the setting of a custom DateTime.UtcNow implementation for testing. 626 | By default this will be a call to 627 | 628 | 629 | 630 | 631 | Resets the custom implementations to their defaults. 632 | Should be called during test teardowns. 633 | 634 | 635 | 636 | 637 | A readonly dictionary of string key / object value pairs 638 | 639 | 640 | 641 | 642 | Fluent API for defining a Circuit Breaker . 643 | 644 | 645 | 646 | 647 | Builds a that will function like a Circuit Breaker. 648 | The circuit will break after 649 | exceptions that are handled by this policy are raised. The circuit will stay 650 | broken for the . Any attempt to execute this policy 651 | while the circuit is broken, will immediately throw a containing the exception 652 | that broke the cicuit. 653 | 654 | If the first action after the break duration period results in an exception, the circuit will break 655 | again for another , otherwise it will reset. 656 | 657 | 658 | The policy builder. 659 | The number of exceptions that are allowed before opening the circuit. 660 | The duration the circuit will stay open before resetting. 661 | The policy instance. 662 | (see "Release It!" by Michael T. Nygard fi) 663 | exceptionsAllowedBeforeBreaking;Value must be greater than zero. 664 | 665 | 666 | 667 | --------------------------------------------------------------------------------