Skip to main content
 首页 » 编程设计

c#之来自 WPF 的异步 Web 服务请求

2024年02月01日24开发

我已经尝试(但失败了)从 WPF 应用程序进行异步 Web 服务调用。

我在我的代码中实现了一个 BackgroundWorker,当您在我的 GUI 上按下“发送”按钮时,它应该可以完成这项工作。它做了它应该做的事,有时,但最终它实际上并没有异步运行。

当您在我的 GUI 中按下按钮时,将触发以下代码:

private void btnSend_Click(object sender, RoutedEventArgs e) 
{ 
    sQuantity = boxQuantity.Text; 
    progressBar.Maximum = double.Parse(sQuantity); 
    worker.RunWorkerAsync(); 
} 

sQuantity 只是一个带有数字的框。它将确定您一次向网络服务发送了多少请求。

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 线程,代码是异步的;你问的是什么并发性。任何类型的复杂 I/O 工作最好用 async 完成。/await ,所以我要扔掉你的后台工作人员,直接使用 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(); 
  } 
}