如何使用Rx监视文件的变化?

时间:2014-10-24 18:26:48

标签: c# linq system.reactive

我正在使用Rx来监控文件的更改。

         |  window1   | |  window2   | | window3 |
  Time   01 02 03 04 05 06 07 08 09 10 11 12 13 14 15

 Input       M  M     M     M     M              R
Output                M              M           R

 Input       M  M     M     M     M        M     R
Output                M              M     M     R         

(M - 修改,R - 重命名)

热门示例:最后一个窗口(11 - 14)没有剩余修改。

底部示例:最后一个窗口中的剩余修改(在t = 12)。

我上面的示例我每5秒采样一次以将修改操作限制为文件。即在每5秒窗口中,我只采用最新的修改操作。

我希望在重命名操作发生后立即停止采样 返回当前窗口中的最新修改(如果有)重命名事件。

有些注意事项:

  1. 最后一个窗口不需要5秒钟。
  2. 最后一个窗口可以返回多个事件。
  3. 这是我到目前为止所拥有的。

    var events = source
                .GroupBy(e => e.FullPath)
                .SelectMany(g => g.Sample(TimeSpan.FromSeconds(5)));
    

1 个答案:

答案 0 :(得分:1)

这是一个解决方案 - 有点匆忙,所以我确信它可以整理,但似乎有效。这使用了Nuget包rx-testingix-main [sic - IEnumerable<T>.TakeLast()]所需的。我已经将您的示例映射为已呈现,但是将一个时间单位表示为100个刻度。

请注意,您的问题中的第二个方案有一个错误 - 在收到重命名事件之前,无法发出上次修改的事件。由于这个原因,我的输出在1400时刻发出了两个事件。

主要查询如下(假设actions包含FileAction个事件):

发布操作,以便我们可以多次订阅:

actions.Publish(source => {

以500个刻度间隔显示操作,但在重命名时关闭窗口:

  return source.Window(
    () => Observable.Timer(TimeSpan.FromTicks(500), scheduler)
                    .Merge(source.Where(x => x == FileAction.Rename)
                                 .Select(_ => 0L)))

停止Rename上的窗口流:

    .TakeUntil(source.Where(x => x == FileAction.Rename))

将窗口展平为包含最后两个事件的列表流:

    .SelectMany(x => x.TakeLastBuffer(2))

查找重命名作为最后一项,如果是重命名,则获取最后2项,否则只是最后一项。请注意,这是精心设计的,用于处理空列表或1个项目的列表。此外,再次使用SelectMany将列表展平,以便我们留下一系列操作。

    .SelectMany(l => {
      return l.LastOrDefault() == FileAction.Rename
        ? l.TakeLast(2)
        : l.TakeLast(1);
    });
})

您可以订阅此内容以获取所请求的活动。以下是完整示例:

void Main()
{
    var tests = new Tests();
    tests.Scenario();
}

// Note inheritance of ReactiveTest to get OnXXX helpers
public class Tests : ReactiveTest
{
    public enum FileAction
    {
        Modify,
        Rename
    }

    public void Scenario()
    {
        var scheduler = new TestScheduler();
        var actions = scheduler.CreateHotObservable<FileAction>(
            OnNext(200, FileAction.Modify),
            OnNext(300, FileAction.Modify),
            OnNext(500, FileAction.Modify),
            OnNext(700, FileAction.Modify),
            OnNext(900, FileAction.Modify),
            // Uncomment next line for
            // scenario with mod + rename in last window
            // OnNext(1200, FileAction.Modify),
            OnNext(1400, FileAction.Rename));

        actions.Publish(source => {
            return source.Window(
                () => Observable.Timer(TimeSpan.FromTicks(500), scheduler)
                                .Merge(source.Where(x => x == FileAction.Rename)
                                            .Select(_ => 0L)))
                .TakeUntil(source.Where(x => x == FileAction.Rename))
                .SelectMany(x => x.TakeLastBuffer(2))
                .SelectMany(l => {
                    return l.LastOrDefault() == FileAction.Rename
                        ? l.TakeLast(2)
                        : l.TakeLast(1);
                });
        })
        .Timestamp(scheduler) /* to show timings */
        .Subscribe(x => Console.WriteLine(
            "Event: {0} Timestamp: {1}",
            x.Value, x.Timestamp.Ticks));

        scheduler.Start();

    }
}

输出:

Event: Modify Timestamp: 500
Event: Modify Timestamp: 1000
Event: Rename Timestamp: 1400

如果您取消注释第二种情况的标记线,则会获得:

Event: Modify Timestamp: 500
Event: Modify Timestamp: 1000
Event: Modify Timestamp: 1400
Event: Rename Timestamp: 1400