簡體   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