[英]Async Web Service Requests from WPF
我嘗試過(但失敗了)從WPF應用程序進行異步Web服務調用。
當您在GUI上按“發送”按鈕時,我在我的代碼中實現了應該執行該工作的BackgroundWorker
。 它有時會執行預期的操作 ,但最終實際上並沒有異步運行。
當您在我的GUI中按下按鈕時,將觸發以下代碼:
private void btnSend_Click(object sender, RoutedEventArgs e)
{
sQuantity = boxQuantity.Text;
progressBar.Maximum = double.Parse(sQuantity);
worker.RunWorkerAsync();
}
sQuantity
只是一個帶有數字的盒子。 它將確定您一次發送到Web服務的請求數量。
progressBar
是您所期望的:進度條。
worker.RunWorkerAsync()
是我調用DoWork
方法的地方。 看起來像這樣:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
EnableButton(false);
List<LoanRequestNoCreditScoreDTO> dtoList = GetData();
foreach (LoanRequestNoCreditScoreDTO dto in dtoList)
{
using (LoanBrokerWS.LoanBrokerWSClient client = new LoanBrokerWS.LoanBrokerWSClient())
{
try
{
Task<LoanQuoteDTO> lq = RequestQuote(dto, client);
LoanQuoteDTO response = lq.Result;
lq.Dispose();
String responseMsg = response.SSN + "\n" + response.interestRate + "\n" + response.BankName + "\n------\n";
AppendText(responseMsg);
worker_ProgressChanged();
}
catch (Exception ex)
{
AppendText(ex.Message + "\n" + ex.InnerException.Message + "\n");
worker_ProgressChanged();
}
}
}
EnableButton(true);
}
最終,這是我理所當然的地方。 我希望應用程序發送與用戶指定數量一樣多的請求。 因此,如果我寫了10封,我將發送10封請求。 RequestQuote()
方法調用以下代碼:
private async Task<LoanQuoteDTO> RequestQuote(LoanRequestNoCreditScoreDTO dto, LoanBrokerWS.LoanBrokerWSClient client)
{
LoanQuoteDTO response = await client.GetLoanQuoteAsync(dto.SSN, dto.LoanAmount, dto.LoanDuration);
return response;
}
如何使DoWork
方法實際發送請求異步?
就UI線程而言,代碼原樣是異步的; 您要問的並發性是什么。 最好使用async
/ await
完成任何類型的復雜I / O工作,因此我將淘汰您的后台工作人員,僅使用直接async
。
首先,按鈕處理程序將處理其自己的啟用/禁用和執行主要下載:
private async void btnSend_Click(object sender, RoutedEventArgs e)
{
var quantity = int.Parse(boxQuantity.Text);
btnSend.Enabled = false;
await DownloadAsync(quantity);
btnSend.Enabled = true;
}
主要下載將創建一個限速SemaphoreSlim
(一種用於限制並發異步操作的通用類型),並等待所有單獨下載完成:
private async Task DownloadAsync(int quantity)
{
var semaphore = new SemaphoreSlim(quantity);
var tasks = GetData().Select(dto => DownloadAsync(dto, semaphore));
await Task.WhenAll(tasks);
}
單獨的下載將各自先限制自己的速率,然后再進行實際的下載:
private async Task DownloadAsync(LoanRequestNoCreditScoreDTO dto, SemaphoreSlim semaphore)
{
await semaphore.WaitAsync();
try
{
using (LoanBrokerWS.LoanBrokerWSClient client = new LoanBrokerWS.LoanBrokerWSClient())
{
var response = await RequestQuoteAsync(dto, client);
}
}
finally
{
semaphore.Release();
}
}
對於執行進度報告,我建議使用用於該模式的類型( IProgress<T>
/ Progress<T>
)。 首先,您決定要在進度報告中使用哪些數據; 在這種情況下,它可能只是一個字符串。 然后,創建進度處理程序:
private async void btnSend_Click(object sender, RoutedEventArgs e)
{
var quantity = int.Parse(boxQuantity.Text);
var progress = new Progress<string>(update =>
{
AppendText(update);
progressBar.Value = progressBar.Value + 1;
});
progressBar.Maximum = ...; // not "quantity"
btnSend.Enabled = false;
await DownloadAsync(quantity, progress);
btnSend.Enabled = true;
}
(請注意,原始代碼中的progressBar.Maximum = double.Parse(sQuantity);
是錯誤的;您應將其設置為下載總數 )。
然后IProgress<string>
剛被傳遞:
private async Task DownloadAsync(int quantity, IProgress<string> progress)
{
var semaphore = new SemaphoreSlim(quantity);
var tasks = GetData().Select(dto => DownloadAsync(dto, semaphore, progress));
await Task.WhenAll(tasks);
}
當您有進度報告時,可以使用該實例:
private async Task DownloadAsync(LoanRequestNoCreditScoreDTO dto, SemaphoreSlim semaphore, IProgress<string> progress)
{
await semaphore.WaitAsync();
try
{
using (LoanBrokerWS.LoanBrokerWSClient client = new LoanBrokerWS.LoanBrokerWSClient())
{
var response = await RequestQuoteAsync(dto, client);
progress.Report(response.SSN + "\n" + response.interestRate + "\n" + response.BankName + "\n------\n");
}
}
catch (Exception ex)
{
progress.Report(ex.Message + "\n" + ex.InnerException.Message + "\n");
}
finally
{
semaphore.Release();
}
}
如果您使用await關鍵字調用RequestQuote,則在每次調用中它都將等待響應,並且您無法進行其他響應。 因此,只需將代理的“ 完成的事件處理程序 ”用於異步方法,然后再調用RequestQuote方法並在事件處理程序中給出響應即可。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.