ReactiveUI 6异步命令未在WPF应用程序中的后台线程上运行
视图模型
public class MyViewModel:ReactiveObject, IRoutableViewModel{ private ReactiveList _appExtensions; public MyViewModel(IScreen screen){ HostScreen = screen; AppExtensions = new ReactiveList(); GetApplicationExtensions = ReactiveCommand.CreateAsyncTask(x => _schemaService.GetApplicationExtensions()); // returns a Task<IEnumerable> 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 AppExtensions { get { return _appExtensions; } set { this.RaiseAndSetIfChanged(ref _appExtensions, value); } } public ReactiveCommand<IEnumerable> GetApplicationExtensions { get; protected set; } }
并且View有一个 。
实现GetApplicationExtensions
public async Task<IEnumerable> GetApplicationExtensions() { IEnumerable 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>(json); } } return extensions; }
从我读过的关于ReactiveUI的所有内容和我见过的所有示例(虽然新的6.0+版本中只有极少数),这应该使我的异步调用(通过HttpClient
进行异步HTTP请求)在后台运行从结果返回时,在我的视图中更新并更新ListBox
。 但是,情况并非如此 – UI在异步调用期间被锁定。 我究竟做错了什么?
UPDATE
如果我将HTTP调用包装在一个Task
那么一切都按预期工作 – UI根本没有挂起。 所以我的服务调用的新实现是这样的:
public Task<IEnumerable> GetApplicationExtensions() { var extensionsTask = Task.Factory.StartNew(async () => { IEnumerable 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>(json); } } return extensions; } return extensionsTask.Result; }
此外,通过对我的异步服务调用的此更改,我可以从我的ReactiveCommand
删除ObserveOn
和SubscribeOn
,如@PaulBetts suggessted。 所以我的视图模型的构造函数中的ReactiveCommand
实现变为:
GetApplicationExtensions = ReactiveCommand.CreateAsyncTask(x => _schemaService.GetApplicationExtensions()); // returns a Task<IEnumerable> GetApplicationExtensions .Subscribe(p => { using (_appExtensions.SuppressChangeNotifications()) { _appExtensions.Clear(); _appExtensions.AddRange(p); } });
你能展示_schemaService.GetApplicationExtensions()
的实现吗?
根据它的实现方式,它可能实际上不在另一个线程上。 可以说,ReactiveCommand应该保证即使在运行异步操作之前意外烧毁CPU的异步操作也会强制进入后台线程,但在这种情况下,效率胜过防御性编程。
您不应该需要SubscribeOn
或ObserveOn
,ReactiveCommand已经保证将在UI线程上返回值。 否则,这段代码看起来不错!
更改
GetApplicationExtensions .ObserveOn(RxApp.MainThreadScheduler) .SubscribeOn(RxApp.TaskpoolScheduler)
至
GetApplicationExtensions .SubscribeOn(RxApp.TaskpoolScheduler) .ObserveOn(RxApp.MainThreadScheduler)
ObserveOn应该在SubscribeOn之后