繁体   English   中英

如何使用Rx监视项目文件和外部更改的文件?

[英]How to use Rx to monitor a project file and files for external changes?

我想重现Visual Studio的行为,当您从外部触摸项目文件时,它会通知您并建议重新加载它!

由于要求,我相信反应式解决方案非常适合解决该问题。

我正在使用本文中介绍的经过修改的反应式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不提供有关哪个进程修改了文件的信息,因此您很不走运。 我想到的可能性很少:

  1. 忽略标志 -应用程序进行更改时,可以设置标志,并在设置标志时忽略事件。 这是最简单的方法,但是如果在设置标志时同时发生外部更改,则可能会错过一些外部更改,并且由于进行限制而使其变得更加复杂。
  2. 标记文件 -每当您对文件进行更改时,都会生成一个GUID(或类似文件),用于标记该文件。 然后,每当触发文件更改时,您都要检查文件属性(可以存储为真实文件系统文件属性-例如,类似于在文件资源管理器中详细看到的jpeg元数据,还有更多设置这种文件属性的方法)然后,如果标签与您所拥有的标签不同或缺少标签,那么您就知道它是外部标签-由于节流和标签过时等原因,您还需要注意
  3. Minifilter文件系统驱动程序 -这将是最干净的解决方案,并且可能与Visual Studio正在使用的驱动程序非常接近-尽管只是个猜测。 它基本上是监视所有I / O更改的通用Windows驱动程序。 Microsoft已经创建了称为minispy的参考实现,它是监视和记录系统中发生的任何I / O和事务活动的小型工具。 您不必自己实现驱动程序,因为在github上已经有使用此方法实现的第三方FileSystemWatcher 该文件系统监视程序提供了哪个进程已修改文件的信息。 唯一的问题是,驱动程序本身必须先安装,然后才能使用,因此您需要具有管理特权的安装程序。

目前,我能想到的就是这些。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM