C# 如何使用Rx监控项目文件和外部更改文件?
我想重现VisualStudio的行为,它会在外部接触项目文件时通知您,并建议重新加载它 由于需求,我认为反应式是解决该问题的最佳匹配 我正在使用本文中描述的修改过的反应式FileSystemWatcher: 当我从项目中删除或添加文件时,它是这样使用的C# 如何使用Rx监控项目文件和外部更改文件?,c#,.net,system.reactive,C#,.net,System.reactive,我想重现VisualStudio的行为,它会在外部接触项目文件时通知您,并建议重新加载它 由于需求,我认为反应式是解决该问题的最佳匹配 我正在使用本文中描述的修改过的反应式FileSystemWatcher: 当我从项目中删除或添加文件时,它是这样使用的 OnProjectChanged(new FileChanged(archive.Filename, FileChangeTypes.Deleted, SourceChangeTypes.Project));
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或类似的guid,用于标记文件。然后,每当触发文件更改时,您都会检查文件属性是否可以存储为真实的文件系统文件属性-例如类似于您在文件资源管理器中详细看到的jpeg元数据,有更多的方法来设置这样的文件属性,如果标签与您拥有的不同或缺少的标签,那么您知道它是外部的-在这里,您还需要注意节流和标签过时等问题 Minifilter文件系统驱动程序-这将是最干净的解决方案,可能与Visual studio使用的非常接近-只是一个猜测。它基本上是一个通用的windows驱动程序,可以监视任何I/O更改。Microsoft创建了名为的参考实现,这是一个监视和记录系统中发生的任何I/O和事务活动的小工具。您不必自己实现驱动程序,因为在github上已经有一个使用这种方法实现的驱动程序。文件系统监视程序提供了哪个进程修改了文件的信息。这里唯一的问题是驱动程序本身需要先安装,然后才能使用,所以您需要管理员特权的安装程序。
目前我所能想到的就是这些。因此,当您保存它以及其他内容时,当前所有内容的简短部分会通知您。好您可以创建一个标志,表明filewatcher拾取和删除的是我,如果是这样的话,或者关闭watcher,保存并重新打开它。假设您的应用程序保存需要很长时间,同时用户也更改了文件系统中的某些内容,那么您将不会收到通知,因为filewatcher已关闭。关于标志,这是我们对FileChanged所做的,我认为如果您的文件正在保存,它应该被锁定!如果我们在同一个文件上讨论更改,这是正确的;从我的应用程序保存,尝试从windows资源管理器更改。我的意思是,如果关闭FileSystemWatcher,我可能会丢失由Windows Explorer用户修改的项目其他文件的一些信息,这就是为什么使用标志更好的原因。。因为如果file=file我关心的,或者只监视打开的文件,那么可以使用某种形式的结构
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));
}