代碼:html
public interface IFileProvider { /// <summary> /// Locate a file at the given path. /// </summary> /// <param name="subpath">Relative path that identifies the file.</param> /// <returns>The file information. Caller must check Exists property.</returns> IFileInfo GetFileInfo(string subpath); /// <summary> /// Enumerate a directory at the given path, if any. /// </summary> /// <param name="subpath">Relative path that identifies the directory.</param> /// <returns>Returns the contents of the directory.</returns> IDirectoryContents GetDirectoryContents(string subpath); /// <summary> /// Creates a change trigger with the specified filter. /// </summary> /// <param name="filter">Filter string used to determine what files or folders to monitor. Example: **/*.cs, *.*, subFolder/**/*.cshtml.</param> /// <returns>An <see cref="IExpirationTrigger"/> that is triggered when a file matching <paramref name="filter"/> is added, modified or deleted.</returns> IExpirationTrigger Watch(string filter); }
應用的例子以下:async
var provider = new PhysicalFileProvider(Environment.CurrentDirectory); var info = provider.GetFileInfo("File.txt"); var info2= provider.GetFileInfo("/File.txt"); var dir1= provider.GetFileInfo(""); var dir2= provider.GetFileInfo("sub"); var dir3= provider.GetFileInfo("/sub");
Watch 的實現代碼:ide
public IExpirationTrigger Watch(string filter) { if (filter == null) { return NoopTrigger.Singleton; } // Relative paths starting with a leading slash okay if (filter.StartsWith("/", StringComparison.Ordinal)) { filter = filter.Substring(1); } // Absolute paths not permitted. if (Path.IsPathRooted(filter)) { return NoopTrigger.Singleton; } return _filesWatcher.CreateFileChangeTrigger(filter); }
Watch 的示例:oop
var expirationTrigger = provider.Watch(fileName); int invocationCount = 0; expirationTrigger.RegisterExpirationCallback(_ => { invocationCount++; }, null);
首先Watch一個文件或者文件夾 ---- expirationTrigger = _triggerCache.GetOrAdd(fileName, new FileChangeTrigger(fileName));測試
當文件系統 有變動時:Created Deleted Changed Renamed 時 (這裏指的是 root 文件夾:FileSystemWatcher(root);)ui
首先調用方法 OnFileSystemEntryChange ---須要查找 _triggerCache.ContainsKey(relativePath)this
若是triggerCache 中匹配指定的文件或者文件夾 而後調用 ReportChangeForMatchedEntries orm
而後: _triggerCache.TryRemove(pattern, out expirationTrigger) htm
繼續調用 expirationTrigger.Changed(); 實現代碼爲: TokenSource.Cancel();blog
最後若是註冊有方法 則會調用 註冊的 callback 方法。
注意:這個Watch 是一次性的 就是變動後,下一次變動 註冊的方法就再也不調用了。
能夠經過測試用例來驗證:
public async Task FileTrigger_NotTriggered_After_Expiry() { var fileName = Guid.NewGuid().ToString(); var fileLocation = Path.Combine(Path.GetTempPath(), fileName); File.WriteAllText(fileLocation, "Content"); var provider = new PhysicalFileProvider(Path.GetTempPath()); var expirationTrigger = provider.Watch(fileName); int invocationCount = 0; expirationTrigger.RegisterExpirationCallback(_ => { invocationCount++; }, null); // Callback expected for this change. File.AppendAllText(fileLocation, "UpdatedContent1"); // Callback not expected for this change. File.AppendAllText(fileLocation, "UpdatedContent2"); // Wait for callbacks to be fired. await Task.Delay(WAIT_TIME_FOR_TRIGGER_TO_FIRE); invocationCount.ShouldBe(1); provider.GetFileInfo(fileName).Delete(); }