简体   繁体   English

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

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

I would like to reproduce the behavior of Visual Studio which informs you when a project file is touched externally and proposes to reload it! 我想重现Visual Studio的行为,当您从外部触摸项目文件时,它会通知您并建议重新加载它!

Due to the requirements, I believe reactive is a great match to solve that problem. 由于要求,我相信反应式解决方案非常适合解决该问题。

I am using a modified reactive FileSystemWatcher described in this post: http://www.jaylee.org/post/2012/08/26/An-update-to-matthieumezil-Rx-and-the-FileSystemWatcher.aspx 我正在使用本文中介绍的经过修改的反应式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
}

Now in my application I created an event 现在在我的应用程序中,我创建了一个事件

    private ProjectChangedEventHandler ProjectChanged { get; set; }

    private void OnProjectChanged(FileChanged fileChanged)
    {
        ProjectChanged?.Invoke(this, fileChanged);
    }

    public delegate void ProjectChangedEventHandler(object sender, FileChanged fileChanged);

Which is used like this when I delete or a add a file from the project 当我从项目中删除或添加文件时使用这种方式

        OnProjectChanged(new FileChanged(archive.Filename, FileChangeTypes.Deleted, SourceChangeTypes.Project));

        OnProjectChanged(new FileChanged(archive.Filename, FileChangeTypes.Added, SourceChangeTypes.Project));

Now I can start to leverage those two streams and with a join (which needs fine tuning for the left and right duration selector) I am able to detect which file was modified by my application: 现在,我可以开始利用这两个流,并通过一个连接(需要对左右持续时间选择器进行微调),我可以检测到我的应用程序修改了哪个文件:

    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));
    }

My issue is that I also want to know that a file was modified externally! 我的问题是我也想知道文件是在外部修改的! Any idea would be really helpful! 任何想法都将真正有帮助! Thanks 谢谢

Unfortunatelly the FileSystemWatcher doesn't provide information which process has modified the file, so you are bit out of luck there. 不幸的是,FileSystemWatcher不提供有关哪个进程修改了文件的信息,因此您很不走运。 There are few possibilities that I can think of: 我想到的可能性很少:

  1. Ignore flag - When your application is doing a change you can set a flag and ignore the events when the flag is set. 忽略标志 -应用程序进行更改时,可以设置标志,并在设置标志时忽略事件。 This is the simplest way, but you might miss some external change if it happens concurrently when the flag is set and also it gets even more complicated due to throttling you have. 这是最简单的方法,但是如果在设置标志时同时发生外部更改,则可能会错过一些外部更改,并且由于进行限制而使其变得更加复杂。
  2. Tagging the file - whenever you do a change to the file you generate a guid (or similar) which you will use to tag the file. 标记文件 -每当您对文件进行更改时,都会生成一个GUID(或类似文件),用于标记该文件。 And then whenever the file change is fired, you check the file property (can be stored either as real filesystem file property - similar for example to jpeg metadata you see in details in file explorer, there are more ways to set such file property) and then if the tag is different from what you have or is missing then you know it is external - there you need to also take care due to throttling and the tag being outdated etc 然后,每当触发文件更改时,您都要检查文件属性(可以存储为真实文件系统文件属性-例如,类似于在文件资源管理器中详细看到的jpeg元数据,还有更多设置这种文件属性的方法)然后,如果标签与您所拥有的标签不同或缺少标签,那么您就知道它是外部标签-由于节流和标签过时等原因,您还需要注意
  3. Minifilter file system driver - This would be the cleanest solution and probably is very close to what Visual studio is using - just a guess though. Minifilter文件系统驱动程序 -这将是最干净的解决方案,并且可能与Visual Studio正在使用的驱动程序非常接近-尽管只是个猜测。 It is basically a universal windows driver that monitors any I/O change. 它基本上是监视所有I / O更改的通用Windows驱动程序。 Microsoft has created reference implementation called minispy , which is small tool to monitor and log any I/O and transaction activity that occurs in the system. Microsoft已经创建了称为minispy的参考实现,它是监视和记录系统中发生的任何I / O和事务活动的小型工具。 You don't have to implement the driver yourself as there is already a 3rd party FileSystemWatcher implemented using this approach on github. 您不必自己实现驱动程序,因为在github上已经有使用此方法实现的第三方FileSystemWatcher That file system watcher provides information which process has modified the file. 该文件系统监视程序提供了哪个进程已修改文件的信息。 The only problem here is that the driver itself needs to be installed, before it can be used, so you need admin privileged installer of sort. 唯一的问题是,驱动程序本身必须先安装,然后才能使用,因此您需要具有管理特权的安装程序。

At the moment that's all I can think of. 目前,我能想到的就是这些。

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

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