我想重现 Visual Studio 的行为,它会在外部接触项目文件并建议重新加载它时通知您!
鉴于需求,我相信响应式非常适合解决该问题。
我正在使用这篇文章中描述的经过修改的响应式(Reactive) FileSystemWatcher:http://www.jaylee.org/post/2012/08/26/An-update-to-matthieumezil-Rx-and-the-FileSystemWatcher.aspx
public class FileWatcher
{
private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static IObservable<FileChanged> ObserveFolderChanges(string path, string filter, TimeSpan throttle, Predicate<string> isPartOfProject)
{
return Observable.Create<FileChanged>(
observer =>
{
var fileSystemWatcher = new FileSystemWatcher(path, filter) { EnableRaisingEvents = true, IncludeSubdirectories = true };
var sources = new[]
{
Observable.FromEventPattern<FileSystemEventArgs>(fileSystemWatcher, "Created")
.Where(IsMaybeAProjectFile)
.Select(ev => new FileChanged(ev.EventArgs.FullPath, FileChangeTypes.Added, SourceChangeTypes.FileSystem)),
Observable.FromEventPattern<FileSystemEventArgs>(fileSystemWatcher, "Deleted")
.Where(IsMaybeAProjectFile)
.Select(ev => new FileChanged(ev.EventArgs.FullPath, FileChangeTypes.Deleted, SourceChangeTypes.FileSystem))
};
return sources.Merge()
.Throttle(throttle)
.Do(changed =>
{
if (Logger.IsDebugEnabled)
{
Logger.Debug($"FileWatcher event [{changed.FileChangeType}] {changed.FullPath}");
}
})
.Finally(() => fileSystemWatcher.Dispose())
.Subscribe(observer);
}
);
}
private static bool IsMaybeAProjectFile(EventPattern<FileSystemEventArgs> ev)
{
return ev.EventArgs.FullPath.EndsWith(".zip") || ev.EventArgs.FullPath.EndsWith(".skye");
}
}
public class FileChanged
{
public string FullPath { get; }
public FileChangeTypes FileChangeType { get; }
public SourceChangeTypes SourceChangeType { get; }
public FileChanged(string fullPath, FileChangeTypes fileChangeType, SourceChangeTypes sourceChangeType)
{
FullPath = fullPath;
FileChangeType = fileChangeType;
SourceChangeType = sourceChangeType;
}
}
[Flags]
public enum FileChangeTypes
{
Added = 1,
Deleted = 2
}
[Flags]
public enum SourceChangeTypes
{
FileSystem = 1,
Project = 2
}
现在在我的应用程序中我创建了一个事件
private ProjectChangedEventHandler ProjectChanged { get; set; }
private void OnProjectChanged(FileChanged fileChanged)
{
ProjectChanged?.Invoke(this, fileChanged);
}
public delegate void ProjectChangedEventHandler(object sender, FileChanged fileChanged);
当我从项目中删除或添加文件时,它是这样使用的
OnProjectChanged(new FileChanged(archive.Filename, FileChangeTypes.Deleted, SourceChangeTypes.Project));
OnProjectChanged(new FileChanged(archive.Filename, FileChangeTypes.Added, SourceChangeTypes.Project));
现在我可以开始利用这两个流并通过连接(这需要对左右持续时间选择器进行微调)我能够检测到我的应用程序修改了哪个文件:
private void ObserveProjectModifications(string projectFilePath)
{
_observeFolderChanges = FileWatcher.ObserveFolderChanges(Path.GetDirectoryName(projectFilePath), "*.*", TimeSpan.FromMilliseconds(500), IsPartOfProject);
_observeProjectChanges = Observable.FromEventPattern<ProjectChangedEventHandler, FileChanged>(h => ProjectChanged += h, h => ProjectChanged -= h).Select(pattern => pattern.EventArgs);
_changes = _observeProjectChanges.Join(_observeFolderChanges, _ => Observable.Never<Unit>(), _ => Observable.Never<Unit>(), ResultSelector).Where(changed => IsPartOfProject(changed.FullPath));
}
private FileChanged ResultSelector(FileChanged fileChanged, FileChanged projectChanged)
{
if (Logger.IsDebugEnabled)
{
Logger.Debug($"ResultSelector File [{fileChanged.FileChangeType}] {fileChanged.FullPath} # Project [{projectChanged.FileChangeType}] {projectChanged.FullPath}");
}
if (fileChanged.FullPath == projectChanged.FullPath)
{
if (fileChanged.FileChangeType == projectChanged.FileChangeType)
{
if (fileChanged.SourceChangeType != projectChanged.SourceChangeType)
{
return projectChanged;
}
return fileChanged;
}
return fileChanged;
}
return fileChanged;
}
private bool IsPartOfProject(string fullPath)
{
if (_projectFileManager.ProjectFilePath.Equals(fullPath)) return true;
return _archives.Values.Any(a => a.Filename.Equals(fullPath));
}
我的问题是我还想知道一个文件被外部修改了!任何想法都会很有帮助!谢谢
最佳答案
不幸的是,FileSystemWatcher 没有提供哪个进程修改了文件的信息,所以你有点不走运。我能想到的可能性很少:
- 忽略标志 - 当您的应用程序进行更改时,您可以设置标志并在设置标志时忽略事件。这是最简单的方法,但如果它在设置标志时同时发生,您可能会错过一些外部更改,而且由于您的限制,它会变得更加复杂。
- 标记文件 - 每当您对文件进行更改时,您都会生成一个 guid(或类似的),您将使用它来标记文件。然后每当文件更改被触发时,您检查文件属性(可以存储为真实文件系统文件属性 - 例如类似于您在文件资源管理器中详细看到的 jpeg 元数据,有更多方法可以设置此类文件属性)和然后,如果标签与您拥有或丢失的标签不同,那么您就知道它是外部的 - 由于节流和标签已过时等原因,您还需要注意
- Minifilter 文件系统驱动程序 - 这将是最干净的解决方案,可能非常接近 Visual studio 使用的解决方案 - 虽然只是猜测。它基本上是一个通用的 Windows 驱动程序,可以监视任何 I/O 更改。 Microsoft 已创建名为 minispy 的引用实现,这是一个用于监视和记录系统中发生的任何 I/O 和事务事件的小工具。您不必自己实现驱动程序,因为已经有一个 3rd party FileSystemWatcher在github上使用这种方法实现。该文件系统观察程序提供了哪个进程修改了文件的信息。这里唯一的问题是驱动程序本身需要先安装,然后才能使用,因此您需要某种具有管理员权限的安装程序。
目前我能想到的就是这些。
关于c# - 如何使用 Rx 监控项目文件和文件的外部更改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48203182/