2016-08-19 43 views
0

我正在開發一個單一的表單應用程序,按下按鈕,使一個簡單的庫存數據庫API查詢,並檢查每個返回的ItemID#對應的圖像可能存在或不存在於由ID#形成的URL。我目前正在通過爲每個URL發送HttpWebRequest.Method =「HEAD」請求來做到這一點,除非catch塊被觸發,否則返回true。C#異步多個Web請求到DataTable - 工作似乎停止不明原因

數據庫查詢可能會返回50-150個零件號碼,並且以這種方式單獨向每個零件發送HEAD請求需要大約5分鐘,這不是生產性的。

我想多任務這個過程使用異步和等待。當我點擊按鈕時,它可以正常工作,以約2 /秒的速率將行異步加載到我的DataGridView中(這並不壞,但如果可能的話,我仍然希望加快速度)。但是,在找到2個成功的URL響應後,它停止加載行,似乎只是放棄,因爲我不知道的原因?並且重新啓用UI的syncContext塊永遠不會執行,因爲工作從未完成。任何人都可以看到什麼可能導致這種情況發生?

我一直在努力鬆散的基礎關閉該文檔:

「如何:使用異步使多個並行的Web請求,並等待(C#)」 https://msdn.microsoft.com/en-us/library/mt674880.aspx

namespace ImageTableTest 
{ 
public partial class ImageTableTestForm : Form 
{ 
    //P21 Authentication Variables 
    private static Token P21token = null; 
    private static RestClientSecurity rcs; 

    //Create Tables and bindingSource 
    DataTable itemDataIMG = new DataTable(); 
    DataTable itemDataNOIMG = new DataTable(); 
    DataTable itemDataComplete = new DataTable(); 
    BindingSource bindingSource = new BindingSource(); 

    private readonly SynchronizationContext synchronizationContext; 


    public ImageTableTestForm() 
    { 
     InitializeComponent(); 

     //Create syncContexct on UI thread for updating UI 
     synchronizationContext = SynchronizationContext.Current; 

     //authenticate database API function 
     authenticateP21();  

     //Designing DataTables 
     itemDataIMG.Columns.Add("MPN#", typeof(string)); 
     itemDataIMG.Columns.Add("IMG", typeof(bool)); 
     itemDataIMG.Columns[1].ReadOnly = true; 

     itemDataNOIMG.Columns.Add("MPN#", typeof(string)); 
     itemDataNOIMG.Columns.Add("IMG", typeof(bool)); 
     itemDataNOIMG.Columns[1].ReadOnly = true; 

     itemDataComplete.Columns.Add("MPN#", typeof(string)); 
     itemDataComplete.Columns.Add("IMG", typeof(bool)); 
     itemDataComplete.Columns[1].ReadOnly = true; 

     //bind to DataGridView itemView 
     bindingSource.DataSource = itemDataComplete;   
     itemView.DataSource = bindingSource; 
     itemView.AutoGenerateColumns = false; 
    } 



    private async void testBtn_Click(object sender, EventArgs e) 
    { 
     //When button is clicked, disable UI and 
     //start background work: 
     testBtn.Enabled = false; 
     loadSpinner.Visible = true; 

     await Task.Run(() => 
     { 
      getItemView(); 
     }); 
    } 


    private async void getItemView() 
    { 
     try 
     { 
      //This executes the query and returns an array of Part objects: 
      PartResourceClient prc = new PartResourceClient(ConfigurationManager.AppSettings["P21.BaseURI"], rcs); 
      prc.QueryFilter("add_to_ebay eq 'Y'"); 
      Part[] pResults = prc.Resource.GetParts();    

      int numParts = pResults.Length;     
      Task<bool>[] taskArray = new Task<bool>[numParts]; 
      bool[] IMGboolArray = new bool[numParts]; 

      //For each part, create CheckImageURL task and add to task Array 
      //Then Await execution 
      for (int i = 0; i < numParts; i++) 
      { 
       taskArray[i] = CheckImageURL(pResults[i].ItemId); 
       IMGboolArray[i] = await taskArray[i]; 
      }     
     } 
     catch (Exception e) 
     { 
      MessageBox.Show(e.ToString()); 
     } 

     //When all Tasks finish, remove loadSpinner, re-enable UI 
     //(This never executes for unknown reasons.) 
     synchronizationContext.Post(new SendOrPostCallback(o => 
     {    
      loadSpinner.Visible = false; 
      testBtn.Enabled = true; 
     }), null); 

     MessageBox.Show("<DONE>"); 
    } 


    async Task<bool> CheckImageURL(string MPN) 
    { 
     //Here I am forming and executing the web HEAD request, 
     //If there is there is a 'NOT FOUND' response it goes to 'catch' block: 
     string URL = "https://s3-us-west-2.amazonaws.com/www.crosscreektractor.com/ebay-images/" + MPN + "_e.png"; 
     HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL); 
     request.Method = "HEAD"; 
     try 
     { 
      await request.GetResponseAsync(); 
      synchronizationContext.Post(new SendOrPostCallback(o => 
      { 
       addDataRows(MPN, true); 
      }), null); 

      return true; 
     } 
     catch 
     { 
      synchronizationContext.Post(new SendOrPostCallback(o => 
      { 
       addDataRows(MPN, false); 
      }), null); 

      return false; 
     } 
    } 

    private void addDataRows(string MPN, bool IMG) 
    { 
     //Add data to respective table: 
     if (IMG) 
     { 
      itemDataIMG.Rows.Add(MPN, IMG); 
     } 
     else 
     { 
      itemDataNOIMG.Rows.Add(MPN, IMG); 
     } 

     //Here I am sorting the IMG and NOIMG tables, 
     //then merging them into the Complete table which 
     //The DataGridView is bound to, so that IMG entries are on top: 
     itemDataIMG.DefaultView.Sort = ("MPN# DESC"); 
     itemDataNOIMG.DefaultView.Sort = ("MPN# DESC"); 

     itemDataComplete.Clear(); 
     itemDataComplete.Merge(itemDataIMG); 
     itemDataComplete.Merge(itemDataNOIMG); 
     itemView.Refresh(); 
    } 
+0

在TAP和異步/等待中錯過了。實現和使用基於任務的異步模式是錯誤的。 –

+0

哈姆雷特,是什麼導致它失速,或者如果做得不同,它會降低開銷嗎?也許你可以提供文檔或告訴我哪些行看起來混亂? – mholberger

+0

google通過關鍵字TAP,async/await,基於任務的異步模式。 –

回答

0

感謝我的TAP模式的建議,肯定學習了很多關於TAP。

我的問題的解決方案是HttpWebRequest連接限制。在成功請求後,它不會自動關閉連接,並且您必須通過抓取WebResponse並關閉它(僅在成功連接時需要):

WebResponse response = await request.GetResponseAsync(); 
{do stuff} 
response.Close(); 
1

更改getItemView()方法爲Task返回,像這樣:

private async Task getItemView() 

然後代替使用的只需await此呼叫在單擊事件處理程序,像這樣:

await getItemView(); 
+0

在CheckImageURL()由於某種原因返回兩次後,它仍然停止添加行。 – mholberger

+0

另外,當我這樣做時,UI在查詢完成時凍結,所以我想我會繼續使用await Task.Run() – mholberger