MVVM XAML 应用程序中的异步等待

Async await in MVVM XAML Applications

我试图了解我应该如何在具有 async/await 模式的 ViewModel 中接收初始数据。我们看代码:

public interface IPeopleService
{
     Task<IEnumerable<Person> GetPeopleAsync();
}


public MainViewModel
{
    public ObservableCollection<Person> People{get;set;}

  public MainViewModel(IPeopleService peopleService)
  {
       LoadMyData(peopleService);
  }

    public async Task LoadMyData(IPeopleService peopleService)
    {
       try
       {
          People = await peopleService.GetPeopleAsync();
       }
       catch(Exception e)
       {
         //log       
         //notify user    
       }
    }

}

有一个 PeopleService,其中包含人员数据的异步方法。它注入了 IOC(无论如何)。之后我调用异步操作 LoadMyData(以防止阻塞 UI,因此没有 await 关键字)并在该方法中我从服务中调用异步操作以捕获所有异常并通知用户。这种方法有什么问题?

我阅读了来自 msdn 的文章:https://msdn.microsoft.com/en-us/magazine/dn605875.aspx,当我注意到为这种任务创建一个通用 class 并更改我的所有属性以使用该 class 作为通用参数时,更改 XAML 绑定以接收 Property.Result 属性,我认为这很疯狂,并且在项目中造成了很大的混乱。此外,ViewModel 中的属性指定此 属性 是异步的,在我看来这是糟糕的设计。解决我的问题的最简单方法是什么?我的解决方案可以让一切变得简单吗?

After that I invoke asynchronous operation LoadMyData (to prevent blocking UI, so no await keyword) and inside that method I invoke asynchronous operation from service catching all exceptions and notyfying user. What's wrong with this approach?

您的 UI 在加载数据时未被阻止。那挺好的。但是异步数据加载带来了一些问题: UI 在数据加载时显示什么? UI 如何向用户显示错误?

在您当前的代码中,UI 列表在加载数据时是空的。 IMO 至少应该有一种 "loading" 状态,以便用户可以区分空结果集和仍在进行中的操作。另外,我假设您的 notify user 代码是通过代码而不是通过数据绑定调出一个对话框或其他东西。 IMO 错误指示器比模态对话框更好。

NotifyTaskCompletion<T> 背后的目的是它充当数据可绑定的异步操作。所以你可以使用它的属性来改变 to/from 到 "loading" 和 "error" 状态。这就是您的绑定必须更改为 .Result 的原因 - 因为您要绑定到异步操作的 result。如果您不想显示 "loading" 指示器并且不想通过数据绑定显示错误,那么是的,NotifyTaskCompletion<T> 就太过分了。

这取决于您想要进行数据绑定的程度。如果您对所有内容都在代码中感到满意,那很好:

public async Task LoadMyData(IPeopleService peopleService)
{
   try
   {
      ... // Hide people display
      ... // Show loading indicator
      People = await peopleService.GetPeopleAsync();
      ... // Show people display
   }
   catch(Exception e)
   {
      ... // Show error indicator
   }
   finally
   {
      ... // Hide loading indicator
   }
}

或者,您可以使用数据绑定来完成这一切,这会使您的代码更简单:

// (wrapped in NotifyTaskCompletion)
public async Task LoadMyData(IPeopleService peopleService)
{
    People = await peopleService.GetPeopleAsync();
}

两种方法我都做过,但如果我有一个执行大量异步操作的项目,我倾向于 NotifyTaskCompletion<T>