2012-12-17 41 views
0

我很難過,我需要你的幫助。WPF> WEBAPI> HttpClient> AWAIT = UI延遲

我有一個WPF應用程序,使用ASYNC & AWAIT調用WEBAPI,我的用戶界面出現延遲。

這裏是我使用的代碼...

HttpResponseMessage response = await WEBAPI.GetHttpClient().GetAsync("api/COStateType/GetStateTypesByCountryID/" + Constants.COUNTRY_ID_USA); 
response.EnsureSuccessStatusCode(); 
App.stateTypes = await response.Content.ReadAsAsync<List<coStateType>>(); 

的GetHttpClient代碼...

public static HttpClient GetHttpClient() 
    { 
     HttpClient client = new HttpClient(); 
     client.BaseAddress = new Uri(ConfigurationManager.AppSettings["ABS2.WEBAPI.Uri"]); 
     client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 
     return client; 
    } 

這裏是主要問題...

在啓動過程中我用移動齒輪(動畫)顯示啓動畫面。實施對WEBAPI的調用後,動畫不再流暢。我讀過使用HttpClient導致UI阻塞的ASYNC調用,這是我的動畫發生的情況。

這裏是問題...

反正有這個問題嗎?我試過在BackgroundWorker和/或Task中包裝這個調用,但都沒有解決這個問題。是否有另一種與WEBAPI進行通信的方式,不會導致此UI被阻塞?我做錯了什麼?

很多,非常感謝任何和所有幫助!

回答

2

@ Stef7 & @Panagiotis - 你們是正確的UI線程被阻止。我不確定我明白爲什麼異步調用會阻止UI線程;我將不得不做更多的研究。

我已經嘗試過一個後臺線程,但直到我在任務上實現了ContinueWith後,才解決了我的問題。

謝謝你的幫助。

這是最終的代碼。

public partial class Splash : Window 
{ 
    private TypeBO boType = new TypeBO(); 
    private Stopwatch swTotal = new Stopwatch(); 
    private Stopwatch swData= new Stopwatch(); 

    public Splash() 
    { 
     InitializeComponent(); 
     swTotal.Start(); 
    } 

    private void GetData() 
    { 
     Assembly assembly = Assembly.GetExecutingAssembly(); 
     lblMessage.Content = "Please Wait..."; 
     lblVersion.Content = "Version: " + assembly.GetName().Version.Major + "." + assembly.GetName().Version.Minor + "." + assembly.GetName().Version.Revision; 

     swData.Start(); 

     var backgroundWorker = new BackgroundWorker(); 
     backgroundWorker.DoWork += this.OnBackgroundWorkerDoWork; 
     backgroundWorker.RunWorkerCompleted += OnBackgroundWorkerRunWorkerCompleted; 
     backgroundWorker.RunWorkerAsync(); 
    } 

    private void OnBackgroundWorkerDoWork(object sender, DoWorkEventArgs e) 
    { 
     WEBAPI.GetHttpClient().GetAsync("api/COStateType/GetStateTypesByCountryID/" 
      + Constants.COUNTRY_ID_USA).ContinueWith(r => 
      { 
       r.Result.EnsureSuccessStatusCode(); 
       e.Result = r.Result.Content.ReadAsAsync<List<coStateType>>().Result; 
      }).Wait(); 

     swData.Stop(); 
     System.Diagnostics.Debug.Write("Splash - DATA - Total Elapsed Time - " + swData.Elapsed + Environment.NewLine); 
    } 

    private void OnBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     var backgroundWorker = sender as BackgroundWorker; 
     backgroundWorker.DoWork -= this.OnBackgroundWorkerDoWork; 
     backgroundWorker.RunWorkerCompleted -= OnBackgroundWorkerRunWorkerCompleted; 

     App.stateTypes = e.Result as List<coStateType>; 

     coStateType pleaseSelectStateType = new coStateType() { State_ID = 0, State_Nm = "Select One", Country_ID = 1 }; 
     App.stateTypes.Insert(0, pleaseSelectStateType); 

     sbHideState.Completed += (snd, eva) => 
     { 
      //Pre-Load User Controls 
      Assembly assembly = this.GetType().Assembly; 
      UserControl ucBHSearch = (UserControl)assembly.CreateInstance(string.Format("{0}.BHSearch", "ABS2.WPF.ADMIN.UserControls.BusinessHierarchy")); 
      UserControl ucBHDetails = (UserControl)assembly.CreateInstance(string.Format("{0}.BHDetails", "ABS2.WPF.ADMIN.UserControls.BusinessHierarchy")); 
      App.userControls.Add("BHD", ucBHDetails); 
      App.userControls.Add("BHS", ucBHSearch); 

      Login winLogin = new Login(); 
      winLogin.Show(); 

      swTotal.Stop(); 
      System.Diagnostics.Debug.Write("Splash - UI - Total Elapsed Time - " + swTotal.Elapsed + Environment.NewLine + Environment.NewLine); 
      swTotal.Reset(); 
      this.Close(); 
     }; 
     sbHideState.Begin(gridSplash); 
    } 

    private void Window_Loaded_1(object sender, RoutedEventArgs e) 
    { 
     lblMessage.Content = ""; 
     lblVersion.Content = ""; 
     sbDefaultState.Begin(gridSplash); 
     sbRotateState.Begin(gridSplash);   
    } 

    private void window_ContentRendered(object sender, EventArgs e) 
    { 
     sbShowState.Completed += (snd, eva) => 
     { 
      GetData(); 
     }; 
     sbShowState.Begin(gridSplash); 
    } 
} 
1

當一個等待完成等待,執行繼續在原來的上下文(線程),在你的情況下是UI線程。

這使您的代碼無需使用調度程序即可訪問UI控件。這意味着在最後的等待之後的任何昂貴的代碼實際上將在UI線程上運行。

如果您希望您的代碼繼續在單獨的線程上運行,則應在調用等待之後添加.ConfigureAwait(false),即。

await response.Content.ReadAsAsync<List<coStateType>>().ConfigureAwait(false); 

在這種情況下,你應該小心訪問使用分派或方法的SynchronizationContext Post/Send,如UI控件。

SynchronizationContext.Current.Post(_=>progressBar.Value+=5,null); 
1

所有發佈的代碼都在UI線程中執行。異步功能可以幫助您不會阻塞UI線程,而無需執行任何操作(在Web請求期間),但是如果讓UI線程忙於過多的代碼,則動畫將始終會出現問題。

我會嘗試將所有網絡相關的代碼移動到後臺工作,並只發布簡短的UI更新到UI線程。既然你寫過,你已經嘗試過一個後臺工作者,我不得不假設UI更新花費很長時間。

如果您有性能分析器,請分析UI線程的繁忙時間以獲得更好的圖像。

1

您需要使用「任務」功能來調用異步。這不會阻止你的GUI主線程,也不需要使用後臺工作者或線程。 「任務」,「異步」,「等待」一起走。