Observing a file system using RX.NET

  Kiến thức lập trình

I am writing an experimental WPF project with ReactiveUI and RX.NET.
In this project I have a viewmodel containing an IEnumerable, which represents file paths.
The viewmodel:

  public class MainWindowViewModel : ReactiveObject
  {
      private string _selectedPath;
      private ReactiveFileSystemWatcher _fileSystemWatcher;
      private readonly ObservableAsPropertyHelper<IEnumerable<string>> _files;
    public MainWindowViewModel()
    {
        SelectPathCommand = ReactiveCommand.Create(ChoosePath, outputScheduler: RxApp.MainThreadScheduler);

        _files = this.WhenAnyValue(vm => vm.SelectedPath) //when i choose a new path it automatically refreshes the list
            .Skip(1) //skipping the first empty value
            .SelectMany(newPath =>
            {
                _fileSystemWatcher?.Dispose();
                _fileSystemWatcher = new ReactiveFileSystemWatcher(newPath, true);
                return _fileSystemWatcher.GetAllRelevantChanges()
                    .StartWith(_fileSystemWatcher.GetInitialFileArgs()) //prepending the files already in the folder
                    .Scan(new List<string>(), (fileList, args) =>
                    {
                        switch (args.ChangeType)
                        {
                            case WatcherChangeTypes.Created:
                                if (!fileList.Contains(args.FullPath))
                                    fileList.Add(args.FullPath);
                                break;
                            case WatcherChangeTypes.Deleted:
                                if (fileList.Contains(args.FullPath))
                                    fileList.Remove(args.FullPath);
                                break;
                            case WatcherChangeTypes.Changed:
                                if (!fileList.Contains(args.FullPath))
                                    fileList.Add(args.FullPath);
                                break;
                            default:
                                if (args is RenamedEventArgs e)
                                {
                                    if (fileList.Contains(e.OldFullPath))
                                        fileList.Replace(e.OldFullPath, e.FullPath);
                                    else fileList.Add(e.FullPath);
                                }
                                break;
                        }
                        return new List<string>(fileList);
                    })
                    .ObserveOn(RxApp.MainThreadScheduler);
            }).ToProperty(this, x => x.Files, scheduler: RxApp.MainThreadScheduler);
    }
    public IEnumerable<string> Files => _files.Value;
    public string SelectedPath
    {
        get => _selectedPath;
        set => this.RaiseAndSetIfChanged(ref _selectedPath, value);
    }
    public ICommand SelectPathCommand { get; }
    private void ChoosePath()
    {
        this.RaisePropertyChanged(nameof(Files));
        var dialog = new OpenFolderDialog();
        if (dialog.ShowDialog() == true)
        {
            SelectedPath = dialog.FolderName;
        }
    }
}

The ReactiveFileSystemWatcher is class written by me, which is basically a filesystemwatcher wrapper, using Observables. The two relevant functions of it are:

 public IObservable<FileSystemEventArgs> GetAllRelevantChanges() =>
     FileRenamedObservable().Merge(FileDeletedObservable())
             .Merge(CreatedAndChangedObservable());
 public IEnumerable<FileSystemEventArgs> GetInitialFileArgs() =>
     Directory.EnumerateFiles(_path, "*", SearchOption.AllDirectories).Select(filePath => new FileSystemEventArgs(WatcherChangeTypes.Created, System.IO.Path.GetDirectoryName(filePath), System.IO.Path.GetFileName(filePath)));

What i want to do here, is collecting the initial files on a different thread, then loading them into the list in the view, and only then scan for the changes on the observed folder.
Main reason to do this, is to not block the UI thread while doing the file loading, and to show a loading screen while doing so.
In the current implementation, 10000 files would mean that the scan method runs 10000 times to initialize the collection, which runs very slow, but I just cannot figure out how to add my desired logic to the pipeline, while keeping it clean and simple.

Tried creating eventargs from the already existing files and prepending them before the first actual changes, but that produces a really slow program ran sequentially.

1

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website Kho Theme wordpress Kho Theme WP Theme WP

LEAVE A COMMENT