从数据绑定属性设置器中调用异步方法的正确方法?

Ber*_*nig 21 mvvm async-await windows-runtime winrt-async

现在我知道属性不支持异步/等待有充分理由.但有时您需要从属性设置器中启动一些额外的后台处理 - 一个很好的例子是MVVM场景中的数据绑定.

在我的例子中,我有一个绑定到ListView的SelectedItem的属性.当然,我立即将新值设置为支持字段,并完成属性的主要工作.但是,UI中所选项目的更改还需要触发REST服务调用,以根据现在选择的项目获取一些新数据.

所以我需要调用异步方法.显然,我无法等待它,但我也不想激活并忘记呼叫,因为在异步处理期间我可能会错过异常.

现在我的看法如下:

private Feed selectedFeed;
public Feed SelectedFeed
{
    get
    {
        return this.selectedFeed;
    }
    set
    {
        if (this.selectedFeed != value)
        {
            this.selectedFeed = value;
            RaisePropertyChanged();

            Task task = GetFeedArticles(value.Id);

            task.ContinueWith(t =>
                {
                    if (t.Status != TaskStatus.RanToCompletion)
                    {
                        MessengerInstance.Send<string>("Error description", "DisplayErrorNotification");
                    }
                });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

好的,除了事实上我可以将处理从setter转移到同步方法,这是处理这种情况的正确方法吗?有没有更好,更简洁的解决方案,我没有看到?

会非常有兴趣看到其他一些问题.我有点好奇,我无法找到关于这个具体主题的任何其他讨论,因为在MVVM应用程序中,大量使用数据绑定似乎很常见.

Ste*_*ary 12

NotifyTaskCompletion我的AsyncEx库中有一个类型,它本质上是/ 的INotifyPropertyChanged包装器.AFAIK目前与MVVM结合使用的信息非常少,所以如果您发现任何其他方法,请告诉我.TaskTask<T>async

无论如何,NotifyTaskCompletion如果您的任务返回结果,该方法最有效.即,从您当前的代码示例中看,它将GetFeedArticles数据绑定属性设置为副作用,而不是返回文章.如果你做了这个返回Task<T>,你可以得到这样的代码:

private Feed selectedFeed;
public Feed SelectedFeed
{
  get
  {
    return this.selectedFeed;
  }
  set
  {
    if (this.selectedFeed == value)
      return;
    this.selectedFeed = value;
    RaisePropertyChanged();
    Articles = NotifyTaskCompletion.Create(GetFeedArticlesAsync(value.Id));
  }
}

private INotifyTaskCompletion<List<Article>> articles;
public INotifyTaskCompletion<List<Article>> Articles
{
  get { return this.articles; }
  set
  {
    if (this.articles == value)
      return;
    this.articles = value;
    RaisePropertyChanged();
  }
}

private async Task<List<Article>> GetFeedArticlesAsync(int id)
{
  ...
}
Run Code Online (Sandbox Code Playgroud)

然后您的数据绑定可用于Articles.Result获取生成的集合(null直到GetFeedArticlesAsync完成).您可以使用NotifyTaskCompletion"开箱即用"来将数据绑定到错误(例如Articles.ErrorMessage),并且它具有一些布尔便利属性(IsSuccessfullyCompleted,IsFaulted)来处理可见性切换.

请注意,这将正确处理无序完成的操作.由于Articles实际上表示异步操作本身(而不是直接结果),因此在启动新操作时会立即更新.所以你永远不会看到过时的结果.

你不具备使用数据错误处理结合.你可以修改GetFeedArticlesAsync; 你可以做任何你想要的语义; 例如,通过将异常传递给您来处理异常MessengerInstance:

private async Task<List<Article>> GetFeedArticlesAsync(int id)
{
  try
  {
    ...
  }
  catch (Exception ex)
  {
    MessengerInstance.Send<string>("Error description", "DisplayErrorNotification");
    return null;
  }
}
Run Code Online (Sandbox Code Playgroud)

同样,没有内置自动取消的概念,但同样很容易添加到GetFeedArticlesAsync:

private CancellationTokenSource getFeedArticlesCts;
private async Task<List<Article>> GetFeedArticlesAsync(int id)
{
  if (getFeedArticlesCts != null)
    getFeedArticlesCts.Cancel();
  using (getFeedArticlesCts = new CancellationTokenSource())
  {
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

这是当前开发的一个领域,因此请进行改进或API建议!