当 Interaction.Handle 收到重复值时命令抛出异常

Command throws exception when duplicate values are received by Interaction.Handle

我在我的视图模型中实现了以下方法

private async Task OpenAudioFileImpl ()
{
    await OpenFileInteraction.Handle(FileTypes.Audio)
        .Where(fp => !string.IsNullOrWhiteSpace(fp))
        .Where(fp => fp != AudioFilePath)
        .Where(fp => File.Exists(fp))
        .Log(this, $"New audio file path selected")
        .Select(x => AudioFilePath = x);
}

通过此命令调用:OpenAudioFile = ReactiveCommand.CreateFromTask(async _ => await OpenAudioFileImpl());

我认为已注册的处理程序会打开一个 Microsoft.Win32.OpenFileDialog,这样我就可以 select 我想要的文件。

我的目标是仅当 selected 新文件并且该文件实际存在时才更新 AudioFilePath 的值。当我单击按钮时,对话框会正确打开。只要我每次 select 一个不同的文件,我就不会出错并且 AudioFilePath 会正确更新。但是,如果我再次 select 相同的文件或取消对话框,该命令将抛出异常。我做了一些控制台输出,我很确定问题出在这个方法中,我只是不知道它是什么。

由于是 ReactiveCommand 抛出错误,我知道的唯一处理方法是:

OpenAudioFile.ThrownExceptions.Subscribe(ex =>
{
    this.Log().Error("OpenAudioFile failed to execute.", ex);
});

我得到的输出是

Exception thrown: 'System.InvalidOperationException' in System.Reactive.dll
Exception thrown: 'System.InvalidOperationException' in System.Private.CoreLib.dll
MainWindowViewModel: OpenAudioFile failed to execute.

这并没有给我太多信息。

在这些情况下出现异常的原因是因为您正在使用异步等待。等待 observable 的行为是 return 最后一个元素(在 OnCompleted 之前),或者抛出被观察到的 OnError。但是,如果序列不包含任何元素,您将得到一个 InvalidOperationException。所以那些 Where 语句过滤掉了唯一发出的元素。

我更愿意在使用 Rx 时尽可能避免 asnyc/await。但是如果你想坚持使用 async/await,你只需要稍微修改一下你的逻辑。例如,可能删除 Where 语句并在 Select 中进行条件赋值并在其他地方进行验证。不确定你的整体设置,所以也许你可以想出更好的东西。