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;
}
此外,通过对我的异步服务调用的这种更改,我可以删除 ObserveOn
和 SubscribeOn
来 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 的异步操作也会被强制到后台线程,但在这种情况下效率胜过防御性编程。
您不需要 SubscribeOn
或 ObserveOn
,ReactiveCommand 已经保证值将在 UI 线程上返回。否则,这段代码看起来不错!
关于c# - ReactiveUI 6 异步命令未在 WPF 应用程序的后台线程上运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25223340/