c# - 如何使用 Rx 监控项目文件和文件的外部更改?

标签 c# .net system.reactive

我想重现 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 没有提供哪个进程修改了文件的信息,所以你有点不走运。我能想到的可能性很少:

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

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

关于c# - 如何使用 Rx 监控项目文件和文件的外部更改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48203182/

相关文章:

c# - 如果结果来得太晚,则限制但丢弃结果

c# - 命名空间在类中找到,但在 aspx 中找不到

c# - Automapper 从外部类映射到内部类变量

c# - Java 注解 C# 等价物

c# - 如何检查内存映射文件是否存在?

c# - 使用一个可观察量来同步另一个可观察量

c# - 我如何让这个 super 事件监听者平静下来

c# - 如何创建异步 web-api Controller 操作

c# - ListViewItem 选中与选中

c# - 使 SQL Server 表在 Rx 和 C# 中可观察