[英]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.