c# - ReactiveUI 6 异步命令未在 WPF 应用程序的后台线程上运行

标签 c# wpf multithreading asynchronous reactiveui

View 模型

public class MyViewModel:ReactiveObject, IRoutableViewModel{
        private ReactiveList<string> _appExtensions; 

        public MyViewModel(IScreen screen){
            HostScreen = screen;
            AppExtensions = new ReactiveList<string>();

            GetApplicationExtensions =
                ReactiveCommand.CreateAsyncTask(x => _schemaService.GetApplicationExtensions()); // returns a Task<IEnumerable<string>>

            GetApplicationExtensions
                .ObserveOn(RxApp.MainThreadScheduler)
                .SubscribeOn(RxApp.TaskpoolScheduler)
                .Subscribe(p =>
                {
                    using (_appExtensions.SuppressChangeNotifications())
                    {
                        _appExtensions.Clear();
                        _appExtensions.AddRange(p);
                    }
                });

            GetApplicationExtensions.ThrownExceptions.Subscribe(
                ex => Console.WriteLine("Error during fetching of application extensions! Err: {0}", ex.Message));
        }

        // bound to a ListBox 
        public ReactiveList<string> AppExtensions
        {
            get { return _appExtensions; }
            set { this.RaiseAndSetIfChanged(ref _appExtensions, value); }
        } 

       public ReactiveCommand<IEnumerable<string>> GetApplicationExtensions { get; protected set; }
}

并且 View 有一个 <Button Command="{Binding GetApplicationExtensions}"></Button> .

实现GetApplicationExtensions

    public async Task<IEnumerable<string>> GetApplicationExtensions()
    {
        IEnumerable<string> extensions = null;

        using (var client = new HttpClient())
        {
            client.BaseAddress = BaseAddress;
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);

            var response = await client.GetAsync("applications");

            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                extensions = JsonConvert.DeserializeObject<IEnumerable<string>>(json);

            }

        }
        return extensions;
    }

从我读到的关于 ReactiveUI 的所有内容和我看到的所有示例(尽管新的 6.0+ 版本的示例非常少),这应该使我的异步调用(通过 HttpClient 发出异步 HTTP 请求) ) 在后台线程上运行并更新 ListBox在我看来,当结果返回时。然而,情况并非如此——UI 在异步调用期间被锁定。我做错了什么?

更新

如果我将 HTTP 调用包装在 Task 中然后一切都按预期工作 - 用户界面根本没有挂断。所以我的服务调用的新实现是这样的:

    public Task<IEnumerable<string>> GetApplicationExtensions()
    {
        var extensionsTask = Task.Factory.StartNew(async () =>
        {
             IEnumerable<string> extensions = null;

             using (var client = new HttpClient())
             {
                 client.BaseAddress = BaseAddress;
                 client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken);

                 var response = await client.GetAsync("applications");

                 if (response.IsSuccessStatusCode)
                 {
                     var json = await response.Content.ReadAsStringAsync();
                     extensions = JsonConvert.DeserializeObject<IEnumerable<string>>(json);

                 }

             }
             return extensions;
        }

        return extensionsTask.Result;
    }

此外,通过对我的异步服务调用的这种更改,我可以删除 ObserveOnSubscribeOn来 self 的 ReactiveCommand就像@PaulBetts 建议的那样。所以我的 ReactiveCommand我的 View 模型的构造函数中的实现变成了这样:

            GetApplicationExtensions =
                ReactiveCommand.CreateAsyncTask(x => _schemaService.GetApplicationExtensions()); // returns a Task<IEnumerable<string>>

            GetApplicationExtensions
                .Subscribe(p =>
                {
                    using (_appExtensions.SuppressChangeNotifications())
                    {
                        _appExtensions.Clear();
                        _appExtensions.AddRange(p);
                    }
                });

最佳答案

你能展示 _schemaService.GetApplicationExtensions() 的实现吗?

根据它的实现方式,它实际上可能不会在另一个线程上。可以说,ReactiveCommand 应该保证即使是在运行异步操作之前意外消耗 CPU 的异步操作也会被强制到后台线程,但在这种情况下效率胜过防御性编程。

您不需要 SubscribeOnObserveOn,ReactiveCommand 已经保证值将在 UI 线程上返回。否则,这段代码看起来不错!

关于c# - ReactiveUI 6 异步命令未在 WPF 应用程序的后台线程上运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25223340/

相关文章:

c# - 对 SharePoint 列表的 CAML 查询,按查找字段排序

java - 如果我对静态方法进行类级别锁定,并且如果一个线程执行它,那么它会阻止其他线程执行同一类的其他实例方法吗?

ios - 带有串行队列的 CMMotionManager

c# - XAML 绑定(bind)中 IntelliSense 自动完成的强制类型

wpf - 将 XAML 元素绑定(bind)到实体 TwoWay

c# - 如何在 WPF 中单击菜单项时在父窗口下打开子窗口?

java - 如何实现多个 BitTorrent 公告请求的超时?

c# - 如何在多个 Visual Studio 项目之间共享属性? (尤其是 C# 项目)

c# - 检测串口电压

c# - Firefox Webdriver 没有数据可用于编码 437