我有以下場景: 在我的使用 MVVM 的 WPF 應用程式中(我對 MVVM 很陌生)我有一個包含 DataGrid 的視窗。加載視窗時,我想使用物體框架使用資料庫中的條目填充 DataGrid。此外,某個列應在編輯模式下使用 ComboBox,該 ComboBox 也是從 DB 填充的(多對多關系)。這些專案也應該在視窗加載時加載。哦,是的,當然,這應該異步完成,這樣資料庫查詢就不會阻塞 UI。
我已經閱讀了 Stephen Cleary 的這些優秀博客文章:https ://t1p.de/c12y8和https://t1p.de/xkdh。
我選擇了同步構建 ViewModel 并Initialize從 Window-Loaded 事件呼叫異步方法的方法。該Initialize方法然后觸發這兩個查詢。
視圖模型:
public class ViewModel : ViewModelBase
{
// this uses a slightly modified class from the first blog post
private NotifyTaskCompletion databaseAction;
public NotifyTaskCompletion DatabaseAction
{
get => databaseAction;
private set
{
databaseAction = value;
NotifyPropertyChanged();
}
}
public ViewModel()
{
// nothinc asynchronous going on here
}
public void Initialize()
{
DatabaseAction = new NotifyTaskCompletion(InitializeAsync());
}
private async Task InitializeAsync()
{
List<Task> tasks = new List<Task>();
tasks.Add(FirstQueryAsync());
tasks.Add(SecondQueryAsync());
await Task.WhenAll(tasks);
}
private async Task FirstQueryAsync()
{
using (var context = new SampleContext())
{
var query = await context.Beds.ToListAsync();
if (query.Count > 0)
{
beds = new ObservableCollection<Bed>();
query.ForEach(bed => beds.Add(bed));
}
else
{
LoadBedsFromFile(ref beds);
foreach (var bed in beds)
{
context.Beds.Add(bed);
}
await context.SaveChangesAsync();
}
}
}
private void LoadBedsFromFile(ref ObservableCollection<Bed> list)
{
if (File.Exists("Beds.xml"))
{
FileStream fs = new FileStream("Beds.xml", FileMode.Open);
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<Bed>));
list = (ObservableCollection<Bed>)serializer.Deserialize(fs);
fs.Close();
}
}
private async Task SecondQueryAsync()
{
using (var context = new SampleContext())
{
var query = await context.Samples.Where(...)
.Include(...)
.ToListAsync();
foreach (Sample item in query)
{
// each entry is put into a ViewModel itself
SampleViewModel vm = new SampleViewModel(item);
// sampleClass.Samples is an ObservableCollection
sampleClass.Samples.Add(vm);
}
}
}
窗戶:
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
ViewModel vm = this.TryFindResource("viewModel") as ViewModel;
if (vm != null)
vm.Initialize();
}
}
現在這里是問題:UI 沒有回應,并且在初始化完成之前不會更新。即使我使用Task.Run(() => vm.Initialize());. 奇怪的是:如果我Task.Delay()在InitializeAsync方法中添加一個,加載影片會按預期顯示,并且 UI 是回應式的。如果我將延遲放入SecondQueryAsync例如 UI 凍結幾秒鐘,然后加載影片在延遲期間旋轉。我懷疑這可能是創建 DbContext 的一些問題,但我無法確定這一點。
uj5u.com熱心網友回復:
我最終解決了這個問題。TheodorZoulias 的評論和他發布到await Task.Run vs await的鏈接給了我一個解決方案的提示。不幸的是,
替換為引發了其他問題,因為我不能簡單地從該任務的背景關系中修改。new NotifyTaskCompletion(InitializeAsync());new NotifyTaskCompletion(Task.Run(() => InitializeAsync()));ObservableCollection
我真的很喜歡用 async-await 撰寫代碼。問題是當它遇到非異步代碼時。因為我懷疑創建 DbContext 的同步呼叫阻塞了執行緒。
所以這就是我解決它的方法 - 也許它可以幫助某人:
首先,我使用工廠方法來異步創建 DbContext:
public class SampleContext : DbContext
{
private SampleContext() : base()
{
...
}
public static async Task<SampleContext> CreateAsync()
{
return await Task.Run(() => { return new SampleContext(); }).ConfigureAwait(false);
}
}
然后我重寫了查詢資料庫的方法,它們不修改ObservableCollection自己,而是回傳一個串列。第二種方法將回傳值作為引數并修改ObservableCollection. 這樣我就可以ConfigureAwait用來配置背景關系。
private async Task<List<Sample>> SecondQueryAsync()
{
var context = await SampleContext.CreateAsync().ConfigureAwait(false);
var query = await context.Samples.Where(...)
.Include(...)
.ToListAsync().ConfigureAwait(false);
context.Dispose();
return query;
}
private async Task InitializeAsync()
{
List<Task> tasks = new List<Task>();
var firstTask = FirstQueryAsync();
var secondTask = SecondQueryAsync();
tasks.Add(firstTask);
tasks.Add(secondTask);
await Task.WhenAll(tasks);
if (tasks.Any(t => t.IsFaulted))
{
Initialized = false;
}
else
{
Initialized = true;
FillFirstList(firstTask.Result);
FillSecondList(secondTask.Result);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/437699.html
上一篇:使文本框建議過濾器不區分大小寫
