【问题标题】:How to call async method from PropertyChanged?如何从 PropertyChanged 调用异步方法?
【发布时间】:2019-07-09 15:15:26
【问题描述】:

我有一个基于 MVVM 架构的 WPF 应用程序。我正在我的 ViewModel 上实现常用且广泛使用的 INotifyPropertyChanged 接口,因为我需要对用户交互做出反应。

但是我如何在同步 PropertyChanged 事件处理程序中执行异步操作(例如加载一些数据)不使用使用async void'hacks'?

提前致谢!

编辑

我需要避免async void 的主要原因是因为我在测试驱动的环境中工作。异步 void 方法不可测试:(

【问题讨论】:

  • async void 与事件处理程序一起使用并不是一种技巧。这是首选的解决方案。事件处理程序本质上是一劳永逸的,因此不应期望事件处理程序返回任何值。
  • 在属性中加载数据通常被认为是不好的做法。这通常是创建方法而不是使用属性的标准之一。

标签: c# wpf mvvm async-await inotifypropertychanged


【解决方案1】:

其实这和async void无关。

通常您希望触发异步操作并让您的属性设置器返回。 示例 sn-p:

private string carManufacturerFilter;

public string СarManufacturerFilter
{
    get { return carManufacturerFilter; }
    set
    {
        if (carManufacturerFilter != value)
        {
            carManufacturerFilter = value;
            OnPropertyChanged();

            // fire async operation and forget about it here;
            // you don't need it to complete right now;
            var _ = RefreshCarsListAsync();
        }
    }
}

private async Task RefreshCarsListAsync()
{
    // call some data service
    var cars = await someDataService.GetCarsAsync(carManufacturerFilter)
        .ConfigureAwait(false);

    // ...
}

注意,这里有很多东西要补充:

  • 由于这是一劳永逸的方法,您需要阻止用户输入,直到操作运行。换句话说,应该有某种忙碌指示符;
  • 您可能希望延迟异步操作触发。当有字符串属性时,这通常是适用的。您不想在用户输入每个字符后触发异步操作。相反,最好等待用户完成输入;
  • 可能有多个属性触发相同的异步操作(想象一下复杂的数据过滤器)。其中一些应该立即触发操作(例如复选框),其中一些需要延迟才能触发;
  • 您需要在异步方法中处理异常并以某种方式显示错误。

附:我强烈建议你看看Reactive UI

【讨论】:

  • 不会抱怨没有等待等待方法吗?
  • 如果您是关于编译器警告,那么赋值 (var _ = ) 会抑制它。 IRL 这段代码将被包装成类似AsyncCommand 等的东西。这是一个示例,旨在为 OP 提供基本概念。这不是生产就绪代码。
  • 如何用这段代码实现单元测试?
  • @Dennis 在使用async void时任务无法等待时我应该如何断言?
  • 这里没有异步无效。这是常规的异步方法,它返回任务。但无论如何你不能等待它,因为这会使 UI 冻结。
【解决方案2】:

支持async void 的原因是允许在通常无效的事件处理程序中使用await。 如果您希望它是可测试的,请将整个代码写入另一个 async Task 方法并让事件处理程序直接调用它。在您的测试中测试此方法。

void OnPropertyChanged(PropertyChangedEventArgs e)
{
    OnPropertyChangedAsync(e)
}

// Test this method
async Task OnPropertyChangedAsync(PropertyChangedEventArgs e)
{
    ...
}

【讨论】:

    【解决方案3】:

    你不能。 INotifyPropertyChanged 不支持异步调用。您需要进行破解,或重新考虑您的策略。

    INotifyPropertyChanged 不适用于异步操作。它的目标是让一个类通知 UI 其数据已更改。 UI 在专用线程中工作,因此必须避免跨线程操作。

    您应该使用“可怕的”async void 方法。

    您也可以使用Dispatcher.BeingInvoke (async () => { … await …} ),但与使用async void 相同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-06
      • 2016-07-20
      • 1970-01-01
      • 2020-03-10
      • 2015-05-14
      • 1970-01-01
      相关资源
      最近更新 更多