我如何在视图中使用异步方法和绑定更新全局集合?
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 了解更多相关信息。
我正在尝试从数据库中获取一些数据以获得某些组合框所需的项目。我有一个获取所有数据的异步方法,它是从构造函数调用的。代码是这样的
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 了解更多相关信息。