使用带有ReactiveUI的异步API来填充集合的最佳方法是什么



假设我有这个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);
}
}

现在,我已经在ViewModel()构造函数中编写了以下方法来实现LoadModels命令内部的模型加载:

// 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方法中引发的异常将被接受

从异步枚举(IObservable<T>Task<IEnumerable<T>>(或者IObservable<IEnumerable<T>>更好?))填充ReactiveList<T>的"最佳"、最惯用的方法是什么?

在了解了Octokit.net的反应库是如何编写的之后,我编写了以下类以使API适应反应世界:

虽然您有时想要这样做(即展平集合),但通常将其保留为IEnumerable<T>更方便,除非您计划对列表中的每个调用异步方法。既然我们只想把所有东西都放在一个列表中,我们就不想这样做。将其保留为Task<T>

在第一个示例中,我应该处理内部订阅吗?还是在内部可观察对象完成或出错时处理?

每当您在另一个Subscribe中有一个Subscript时,您可能需要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);

相关内容

  • 没有找到相关文章