C# 使用带有ReactiveUI的异步API填充集合的最佳方法是什么?
假设我有这个反应式UI视图模型结构,C# 使用带有ReactiveUI的异步API填充集合的最佳方法是什么?,c#,asynchronous,system.reactive,reactiveui,C#,Asynchronous,System.reactive,Reactiveui,假设我有这个反应式UI视图模型结构,model是任意的模型类型 class ViewModel : ReactiveObject { private readonly ReactiveList<Model> _models; public IReactiveList<Model> Models { get { return _models; } } public IReactiveCommand LoadModels { get; private
model
是任意的模型类型
class ViewModel : ReactiveObject
{
private readonly ReactiveList<Model> _models;
public IReactiveList<Model> Models { get { return _models; } }
public IReactiveCommand LoadModels { get; private set; }
public bool LoadingModels { get; private set; } // Notifies;
}
虽然我认为这两种方法都能奏效,但我有以下疑虑:
- 在第一个示例中,我应该处理内部订阅,还是在内部可观察对象完成或出错时处理
- 在第二个示例中,我知道
方法中引发的异常将被忽略GetModelsAsync
IObservable
或者Task
(或者IObservable
更好)填充ReactiveList
的“最佳”和最惯用的方法是什么
在了解了Octokit.net的反应式库是如何编写的之后,我编写了以下类以使API适应反应式世界:
class ObservableApi
{
private readonly ITaskApi _taskApi;
public IObservable<Model> GetModels() {
return _taskApi.GetModelsAsync().ToObservable().SelectMany(c => c);
}
}
虽然有时需要这样做(即展平集合),但通常更方便的做法是将其保留为IEnumerable
,除非您计划对列表中的每个项调用异步方法。因为我们只想把所有的东西都放在一个列表中,所以我们不想这样做。只需将其保留为Task
在第一个示例中,我应该处理内部订阅,还是在内部可观察对象完成或出错时处理
每当您在另一个Subscribe中有一个Subscribe时,您可能需要SelectMany
操作符。然而,有一个更好的方法来做你想做的事情,你应该查看更多信息
下面是我编写代码的方法:
// In both cases, we want the command to be disabled when loading:
LoadModels = new ReactiveCommand();
LoadModels.RegisterAsyncTask(_ => taskClient.GetModelsAsync())
.Subscribe(items =>
{
// This Using makes it so the UI only looks at the collection
// once we're totally done updating it, since we're basically
// changing it completely.
using (_models.SuppressChangeNotifications())
{
_models.Clear();
_models.AddRange(items);
}
});
LoadModels.ThrownExceptions
.Subscribe(ex => Console.WriteLine("GetModelsAsync blew up: " + ex.ToString());
// NB: _loadingModels is an ObservableAsPropertyHelper<bool>
LoadModels.IsExecuting
.ToProperty(this, x => x.LoadingModels, out _loadingModels);
//在这两种情况下,我们都希望在加载时禁用该命令:
LoadModels=new ReactiveCommand();
LoadModels.RegisterAsyncTask(=>taskClient.GetModelsAsync())
.订阅(项目=>
{
//这种使用使得UI只查看集合
//一旦我们完全完成了更新,因为我们基本上
//完全改变它。
使用(_models.SuppressChangeNotifications())
{
_模型。清除();
_型号。添加范围(项目);
}
});
LoadModels.ThrownException
.Subscribe(ex=>Console.WriteLine(“GetModelsAsync爆炸:”+ex.ToString());
//注意:_loadingModels是一个可观察的属性帮助器
LoadModels.i执行
.ToProperty(这个,x=>x.LoadingModels,out\u LoadingModels);
太棒了。如果我说我不希望你回答,那我就是在撒谎。谢谢。哦,我已经用了不到一个星期的ReactiveUI了,这太棒了。太棒了!如果你有任何问题,请ping StackOverflow,或者如果你不能概括/不想分享,请发邮件paul@paulbetts.orgI已将修改应用为o但是,我有一个问题,那就是this.wheny(x=>LoadingModels,x=>!x.Value)
调用使事情在\u loadingModels
为null
时爆发,这自然是因为只有在LoadModels
初始化后才将其设置为实际值。您是否建议先分配\u loadingModels=observeableaspropertyHelper.Default(false)来解决此问题
或通过使用new ReactiveCommand()
中的allowscocurrentExecution
参数,或者我可以使用new ReactiveCommand()
constructor,它已经将allowscocurrentExecution
设置为false
^;哦,是的,您根本不需要对when执行该操作。只需删除该行。我将修复代码示例
// In both cases, we want the command to be disabled when loading:
LoadModels = new ReactiveCommand(this.WhenAny(x => x.LoadingModels, x => !x.Value));
// First method, with the Observable API;
LoadModels.Subscribe(_ =>
{
LoadingModels = true;
_models.Clear();
observableClient.GetModels().ObserveOnDispatcher()
.Subscribe(
m => _models.Add(m),
onCompleted: () => { LoadingModels = false; });
});
// Second method, with the task API;
LoadModels.Subscribe(async _ =>
{
LoadingModels = true;
try {
var loadedModels = await taskClient.GetModelsAsync();
_models.Clear();
_models.AddRange(loadedModels);
} catch (Exception ex) {
RxApp.DefaultExceptionHandler.OnNext(ex);
} finally {
LoadingModels = false;
}
});
// In both cases, we want the command to be disabled when loading:
LoadModels = new ReactiveCommand();
LoadModels.RegisterAsyncTask(_ => taskClient.GetModelsAsync())
.Subscribe(items =>
{
// This Using makes it so the UI only looks at the collection
// once we're totally done updating it, since we're basically
// changing it completely.
using (_models.SuppressChangeNotifications())
{
_models.Clear();
_models.AddRange(items);
}
});
LoadModels.ThrownExceptions
.Subscribe(ex => Console.WriteLine("GetModelsAsync blew up: " + ex.ToString());
// NB: _loadingModels is an ObservableAsPropertyHelper<bool>
LoadModels.IsExecuting
.ToProperty(this, x => x.LoadingModels, out _loadingModels);