我如何在视图中使用异步方法和绑定更新全局集合?

How could I update a global collection with a async method and binding in the view?

我正在尝试从数据库中获取一些数据以获得某些组合框所需的项目。我有一个获取所有数据的异步方法,它是从构造函数调用的。代码是这样的

private async Task getDataASync()
{
    Task<List<Typ01>> miTsk01 = VariablesGlobales.getData01Async();
    Task<List<Type02>> miTsk02 = VariablesGlobales.getData02Async();
    Task<List<Type03>> miTskT03 = VariablesGlobales.getData03Async();
    Task<List<Type04>> miTsk04 = VariablesGlobales.getData04Async();



    await Task.WhenAll(miTsk01,
    miTsk02,
    miTsk03,
    miTsk04).ConfigureAwait(false);


    GlobalVariables.vgData01.AddRange(miTsk01.Result);
    GlobalVariables.vgData02.AddRange(miTsk02.Result);
    GlobalVariables.vgData03.AddRange(miTsk03.Result);
    GlobalVariables.vgData04.AddRange(miTsk04.Result);
}




public MyViewModel()
{
    getDataASync();
}

XAML

    <ComboBox
      SelectedItem="{Binding MySelectedItem01}"
      ItemsSource="{x:Static vg:VariablesGlobales.vgData01}">
    </ComboBox>

<ComboBox
  SelectedItem="{Binding MySelectedItem02}"
  ItemsSource="{x:Static vg:VariablesGlobales.vgData02}">
</ComboBox>

<ComboBox
  SelectedItem="{Binding MySelectedItem03}"
  ItemsSource="{x:Static vg:VariablesGlobales.vgData03}">
</ComboBox>

<ComboBox
  SelectedItem="{Binding MySelectedItem04}"
  ItemsSource="{x:Static vg:VariablesGlobales.vgData04}">
</ComboBox>

问题是我收到一条错误消息,指出 collectionView 不能被不同于 Dispatcher 的另一个子进程修改。

我试图在我的视图模型中创建一个 属性,它从全局变量中获取数据并且视图绑定这个 属性 然后它就可以工作了。

我真的不太明白这个问题,因为在从数据库获取数据的方法中,它会等到我获取所有数据,然后再更新全局集合。我以为我和主应用程序在同一个线程中,但似乎我错了。

如何将全局集合更新为可以被视图绑定?

谢谢。

有几种方法可以解决这个问题。

最简单的方法就是使用 ConfigureAwait(true) 以确保任何延续都在同一线程上。

await Task.WhenAll(miTsk01,
miTsk02,
miTsk03,
miTsk04).ConfigureAwait(true);

GlobalVariables.vgData01.AddRange( await miTsk01);
GlobalVariables.vgData02.AddRange( await miTsk02);
GlobalVariables.vgData03.AddRange( await miTsk03);
GlobalVariables.vgData04.AddRange( await miTsk04);

几点注意事项

  • 您应该避免使用 Task.Result,因为这会导致死锁,即使任务应该已经完成​​ - 只需使用 await 任务(如上所述)。
  • 您的集合可能应该是 ObservableCollection 而不是 List,这将反映对绑定 UI 控件的 List 的任何更改。这些不支持 AddRange() 作为标准,因此您需要单独添加项目。

另一种方法是使用 class,例如来自 MvvmLight 的 this one,它将在应用程序启动时捕获 UI 上下文,然后可以将其用作 UI 来自不同线程的更新。

Really I don't understand very good the problem, because in the method that get the data from database, it waits until I get all the data and later I update the global collections. I thought that I was in the same thread than the main application, but it seems that I am wrong.

如果您在 UI 线程上调用 getDataASync(),您唯一需要做的就是通过删除对 ConfigureAwait(false):[=21 的调用来捕获上下文=]

await Task.WhenAll(miTsk01,
    miTsk02,
    miTsk03,
    miTsk04);

ConfigureAwait(false) 防止上下文被捕获,这意味着一旦 Task.WhenAll 方法完成,您的 getDataASync() 的其余部分将在线程池线程上执行,而不是在最初调用 getDataASync() 方法的同一线程上执行。

在服务方法中使用 ConfigureAwait(false) 被认为是最佳实践,基本上可以在任何地方使用,但是确实需要上下文的方法,例如填充数据绑定集合的 getDataASync(),是显然是个例外。请参阅 @Stephen Cleary's MSDN Magazine article 了解更多相关信息。