我正在开发一个单一表单应用程序,在按下按钮时,它会进行一个简单的库存数据库 API 查询,并检查每个返回的 ItemID# 是否存在对应的图像,该图像可能存在或不存在于由 ID# 形成的 URL 中。我目前正在通过为每个 URL 发送 HttpWebRequest.Method = "HEAD" 请求来执行此操作,除非触发了 catch 块,否则返回 true。
数据库查询可能会返回 50 - 150 个零件编号,并且以这种方式分别向每个零件发送 HEAD 请求大约需要 5 分钟,而且效率不高。
我正在尝试使用异步和等待来多任务处理这个过程。当我单击该按钮时,它工作正常,以大约 2/秒的速度将行一一异步地加载到我的 DataGridView 中(这还不错,但如果可能的话,我仍然想加快速度)。
但是:在找到 2 个成功的 URL 响应后,它停止加载行并且似乎只是放弃了,原因我不知道???并且重新启用 UI 的 syncContext 块永远不会执行,因为工作永远不会完成。谁能看到可能导致这种情况发生的原因?
我一直在根据这个文档松散地工作:
“如何:使用 async 和 await (C#) 并行发出多个 Web 请求” 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();
}