我有一个WPF应用程序,它通过构造函数中的方法初始化UI的状态。但是,它永远不会从构造函数中的Wait();
返回。
这是我目前通过一个相当人为的样本做的事情:
public class SomeViewModel
{
public ICommand AddDataCommand { get { return RelayCommand(AddDataExecute); } }
public ObservableCollection<int> UIData { /* Property with INotifyPropertyChanged */}
public SomeViewModel()
{
//Load synch. here
LoadData().Wait();
}
public async Task LoadData()
{
UIData = await Task.Run(() => SomeService.SelectAllUIData());
}
public async void AddDataExecute()
{
//Add new data item to database on separate thread to keep UI responsive
await Task.Run(() => SomeService.AddNewData(0));
//Reload data from database and update UI, has to happen after AddNewData completes
await LoadData();
}
}
我相信这是悬而未决,因为我实际上从未归还过Task
。但是,我不知道一种不同的方式来异步分配UIData,它在构造函数和调用它的命令中都有效。
答案 0 :(得分:8)
您看到我在博客上描述的classic deadlock situation。
要解决此问题,请使用async
all the way,正如我在MSDN上有关async
最佳做法的文章中所述。
在某些情况下,这可能很难。我有一篇博客文章描述了async
constructors的几种方法。但是,由于您正在讨论用户界面的ViewModel和数据,您可能会发现async
properties上的博文更有帮助 - 特别是关于数据绑定的部分。
答案 1 :(得分:7)
如果需要构造为异步,请不要通过构造函数构造对象。使用静态工厂方法:
public class SomeViewModel
{
private SomeViewModel()
{ }
public static async Task<SomeViewModel> Create()
{
SomeViewModel model = new SomeViewModel();
await model.LoadData();
return model;
}
public async Task LoadData()
{
UIData = await Task.Run(() => SomeService.SelectAllUIData());
}
//...
}
至于为什么你的代码不工作,你得到了标准await
死锁,你在异步操作中阻塞,阻塞阻止在UI线程上调用continuation,以及有两个不同的操作,每个等待另一个继续,你会陷入僵局。这就是为什么“异步一直”非常重要,而不是同步阻塞异步操作。