让我们说我有这个ReactiveUI视图模型结构,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;
}
这些模型来自这个界面建模的基于任务的异步API:
interface ITaskApi
{
Task<IEnumerable<Model>> GetModelsAsync();
}
在看了Octokit.net的反应库是如何编写之后,我编写了以下类来使API适应被动反应的世界:
class ObservableApi
{
private readonly ITaskApi _taskApi;
public IObservable<Model> GetModels() {
return _taskApi.GetModelsAsync().ToObservable().SelectMany(c => c);
}
}
现在,我已经编写了以下方法来在LoadModels
构造函数的ViewModel()
命令中实现模型的加载:
// 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;
}
});
虽然我认为两种方式都可以胜任,但我有以下疑虑:
GetModelsAsync
方法中引发的异常。从异步枚举中填充ReactiveList<T>
的“最佳”,最惯用的方式是什么(IObservable<T>
或Task<IEnumerable<T>>
(或IObservable<IEnumerable<T>>
会更好?) )?
答案 0 :(得分:2)
在看了Octokit.net的反应库是如何编写之后,我编写了以下类来使API适应被动反应的世界:
虽然您有时想要这样做(即展平集合),但将它保留为IEnumerable<T>
通常会更方便,除非您计划在每个项目上调用异步方法在列表中。由于我们只想将所有内容填入List中,因此我们不想这样做。只需将其保留为Task<T>
在第一个示例中,我应该处理内部订阅还是在内部observable完成或错误时完成?
如果您在另一个订阅中有订阅,您可能会想要SelectMany
运算符。但是,有更好的方法可以执行您要执行的操作,您应该查看this docs article以获取更多信息。
所以,这就是我编写代码的方式:
// 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);