C# 在ReactiveUI/WPF视图模型中,在其他线程填充的可观察对象上调度OAPH的正确方法是什么?

C# 在ReactiveUI/WPF视图模型中,在其他线程填充的可观察对象上调度OAPH的正确方法是什么?,c#,wpf,mvvm,reactiveui,reactivex,C#,Wpf,Mvvm,Reactiveui,Reactivex,我有一个在WPF应用程序启动线程中构建的ReactiveUI视图模型,其中属性是ObservableAsPropertyHelper——从一个单独的任务线程向可观察对象提供的备份视图。设置OAPHs调度程序的正确方法是什么,以避免线程问题,同时保持可测试性,不在UI线程中插入太多内容,也不使viewmodels过于复杂 更详细地说:我最近在一直从事的.NETCore3WPF端项目中选择了一个框架来构建MVVM粘合代码。它是客户端/服务器音频播放系统的客户端,大多数UI的更新来自IObservab

我有一个在WPF应用程序启动线程中构建的ReactiveUI视图模型,其中属性是
ObservableAsPropertyHelper
——从一个单独的
任务
线程向可观察对象提供的备份视图。设置OAPHs调度程序的正确方法是什么,以避免线程问题,同时保持可测试性,不在UI线程中插入太多内容,也不使viewmodels过于复杂

更详细地说:我最近在一直从事的.NETCore3WPF端项目中选择了一个框架来构建MVVM粘合代码。它是客户端/服务器音频播放系统的客户端,大多数UI的更新来自
IObservable
s,这些更新最终附加到事件(使用
FromEventPattern
),这些事件在读卡器
任务中通过TCP连接触发。(不管这是不是一个好主意,我可能会继续问另一个问题。)

我的ViewModels倾向于这样做:

  • 在构造函数中接受上述
    IObservable
    s之一
  • 通过
    .ToProperty
    ,使用
    ObservableAsPropertyHelper
    ,将可观察对象的一些LINQ转换绑定到属性
  • 使用
    .WhenAnyValue
    .ToProperty
    组合绑定这些属性的更多投影
由于我没有购买ReactiveUI绑定和激活系统,ViewModels(我相信)与
App.xaml
的代码隐藏在同一个线程上,并且它们的所有OAPH都在构造函数中初始化。对ViewModel属性的所有访问都是通过单向XAML绑定进行的

为了澄清,视图模型位于一个安装了
ReactiveUI
但未安装
ReactiveUI.WPF
等的项目中;WPF应用程序本身确实安装了
ReactiveUI.WPF

我尝试了几种不同的方法将调度程序提供给
ToProperty
调用及其提供的调度程序:

  • 根本不覆盖任何调度程序。起初,我认为这是正确的,因为我没有提到任何覆盖。此操作失败,出现“无法修改对象,因为这不是创建它的线程”类型异常,可能是因为属性更改通知正在读卡器
    任务
    线程上发生
  • 在不同的位置读取后,在其
    TopProperty
    中覆盖附加到读卡器
    任务的OAPHs调度程序
    的可观察到的
    调度程序:RxApp.MainThreadScheduler
    。这修复了崩溃的异常,但是,当我尝试对视图模型进行单元测试时,我开始出现一些奇怪的调度错误,其中通过服务器更新可观察到的更改没有传播到
    WhenAnyValue
    派生属性
  • 覆盖其他OAPH的调度程序也没有帮助(但可能会稍微干扰故障)。在这个阶段,我感到困惑,因为,所以一切都应该在同一个线程上,并且(希望)按顺序操作
  • 在viewmodel()的构造函数中推入
    调度程序
    ,并在单元测试中将其特别设置为
    立即
    (以及
    RxApp.MainThreadScheduler
    其他地方)
  • 在步骤4之后,下面是我一直在使用的代码的简化版本:

    //表示音频播放UI上的“播放/暂停/停止”控件。
    //UI通过IObservable连接到来自的消息提要
    //播放服务器。
    公共类PlayerTransportViewModel:ReactiveObject
    {
    //保存正在播放的音频的当前“状态”(播放/暂停/停止)。
    私有只读ObservablesPropertyHelper_状态;
    public PlaybackState State=>\u State.Value;
    //为了方便查看,我们将“状态”分解为三个独立的布尔值:
    私有只读可观察属性帮助显示;
    私有只读可观察属性帮助程序;
    私有只读ObservablesPropertyHelper\u已停止;
    public bool isplay=>\u isplay.Value;
    //…等等,等等,等等。
    //然后,构造函数在服务器消息的一些原始提要上获取一个可观察的消息。
    公共播放器TransportViewModel(IObservable事件,isScheduler?scheduler)
    {
    //这种胶水*不*应该*将异步性引入可观察对象。
    var状态=从(事件)中选择播放后台状态的一些LinqGluesSelectingPlaybackStates;
    //这种绑定直接接收由消息解码线程提供的可观察数据。
    //是否需要显式调度程序?
    _state=states.ToProperty(这个,x=>x.state,PlaybackState.Stopped,scheduler:scheduler);
    //这些绑定由whenny从State派生。
    //是否需要显式调度程序?
    _isplay=this.whenyValue(x=>x.State,x=>x==PlaybackState.Playing).ToProperty(this,x=>x.isplay,scheduler:scheduler);
    //…等等,等等,等等。
    }
    }
    
    这段代码现在似乎工作得很好,但我觉得我没有完全理解正确的最小调度程序干扰量应该是多少,甚至我只是修补程序中其他地方的问题。(我在同一个项目的其他部分遇到了一些奇怪的海森堡实例,但我还没有把它们固定在可提问的问题上,所以这是我从我知识的基本差距开始的)