EF 6中的async-await問題

async-await entity-framework entity-framework-6 wpf

我正在嘗試async-await編程與實體框架6(代碼優先)+ WPF,我不明白為什麼在我使代碼異步後UI仍然凍結。以下是我從第一行開始做的事情:

首先,有一個事件處理程序響應單擊按鈕:

private async void LoginButton_Click(object sender, RoutedEventArgs e) {
  if (await this._service.Authenticate(username.Text, password.Password) != null)
    this.Close();
}

然後我在我的服務層中有Authenticate方法:

private async void LoginButton_Click(object sender, RoutedEventArgs e) {
  if (await this._service.Authenticate(username.Text, password.Password) != null)
    this.Close();
}

最後是上下文中的EF代碼:

private async void LoginButton_Click(object sender, RoutedEventArgs e) {
  if (await this._service.Authenticate(username.Text, password.Password) != null)
    this.Close();
}

更新:在一些跟踪之後,UI凍結的原因結果是初始化過程。 UI線程阻塞,直到初始化EF上下文,一旦完成,實際的查詢/保存過程將異步執行。

在單擊處理程序開頭的Task.Yield()上調用後更新 Debug輸出:

private async void LoginButton_Click(object sender, RoutedEventArgs e) {
  if (await this._service.Authenticate(username.Text, password.Password) != null)
    this.Close();
}

一般承認的答案

標記為“異步”的方法仍然是同步的,直到第一次'等待'發生時為止。因此,如果初始代碼中發生的任何事情花費的時間太長(我認為是200毫秒或更長時間,這是WinRT的準則,這似乎是合理的),那麼您可能希望通過先插入await來強制代碼更快地返回。

例如,在您的LoginButton_Click中,您可以插入第一行'await Task.Yield ()',這將允許調用更快地返回到UI線程。

現在,僅通過該更改,由於async / await行為,這些方法仍將在UI線程上運行。我仍然喜歡首先進行更改,因為在很多情況下,這是用戶實際期望發生的事情('async'修飾符在這方面有點令人困惑),而且你可以在處理程序的開頭做一些事情而不必弄亂隨著堆棧的進一步發展。

如果上面的內容不充分(如上下文初始化時間過長,仍然在UI線程上發生,並且仍然凍結UI,只是在稍微不同的時間點)我們可以採取的下一步就是採取那些不合適的部分需要在UI線程上發生並等待知道它們可以在任何線程上處理,而不僅僅是UI線程。對於響應性而言,這通常是一種很好的做法,即使在代碼當前運行“足夠快”而不是明顯問題的情況下也是如此。

為此,我們使用Configure TaskAwait (false)添加到Task。

  • GetUserAsync方法應該在FirstOrDefaultAsync調用之後添加它(“鏈”它)
    • 或者,恕我直言,稍微清潔,是在GetUserAsync方法中刪除async / await關鍵字,只返回從FirstOrDefaultAsync返回的任務。 async / await並不是真的在這個方法中“買”你的東西,恕我直言:)
  • 在Authenticate中,您應該在GetUserAsync調用之後添加它
    • 我不確定的一個潛在的“問題”是,如果CurrentUser是數據綁定到UI。由於它是_service的成員,我猜它不是,但即使它是,我認為 WPF很好,數據綁定項目在非UI線程上更新,它處理將更改封送回UI (調度員?)線程。這與像Silverlight這樣的框架不同,在非框架上更新數據綁定到UI的UI上的屬性將導致相同的跨線程故障,就像您手動更新目標控件一樣。如果我錯了,1)CurrentUser數據綁定到您的UI中 2)非UI線程上的數據綁定更新導致運行時異常,然後避免在此方法中添加ConfigureAwait(false) 。很抱歉這個問題的長度,只是試著聯繫我對這個特殊修改有點不確定。 :)
  • 在LoginButton_Click,我們應該添加它,因為方法(this.Close)的其餘部分需要在UI線程上發生,並ConfigureAwait(假)這裡將打破

一旦這兩個更改都進入,您將1)盡快將控制權返回給調用者(執行與事件處理程序同步的最少量代碼)和2)執行不需要的工作在其他線程上的UI線程上,這應該有希望意味著你的UI不再“凍結”。

如果它在這些更改後仍然凍結,您可能只需要在調試器下運行它,當它凍結時,中斷以查看UI線程的堆棧是什麼來查找有問題的代碼。 :)

祝你好運!




許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因