如何在可取消请求流中重复使用单个异步Web服务调用?

时间:2017-04-10 15:43:04

标签: .net async-await system.reactive

在我的WPF UI中,我有一个客户列表。我还有一个Web API服务,用于获取单个客户的个人资料。服务器端和客户端都使用async / await构建,并且可以取消。

当用户选择"客户A"从ComboBox中,它触发服务器调用以获取客户配置文件。 2秒后(服务器操作方法的设计持续时间),数据返回并显示。如果在那2秒内"客户B"如果选中,我的代码会取消第一个请求并触发第二个请求。

这一切都很有效,忙碌/取消逻辑非常幼稚,如果用户非常快速地选择不同的客户,则无法正确取消。例如,如果我快速按下向下箭头键(组合框具有焦点)10次,则在服务器端正确取消5个请求,而不是5个请求。虽然这不是世界末日,但我不想用可能无缘无故的并行运行的大数据库查询来咀嚼服务器资源。

这是我的客户代码:

CancellationTokenSource _customerProfileCts;

//called when a new customer item is selected from the UI's ComboBox
private async void TriggerGetCustomerProfile()
{
    //if there is already a customer profile fetch operation in progress, we just want to cancel it and start a new one. 
    if (IsBusyFetchingCustomerProfile)
    {
        _customerProfileCts.Cancel();
    }

    try
    {
        IsBusyFetchingCustomerProfile = true;
        IsCustomerProfileReady = false;
        await GetCustomerProfile();
    }
    finally
    {
        IsBusyFetchingCustomerProfile = false;
        _customerProfileCts = null;
    }
}

private async Task GetCustomerProfile()
{
    _customerProfileCts = new CancellationTokenSource();
    await _customerSvc.GetCustomerProfileReport(SelectedCustomer.Id, _customerProfileCts.Token);
    //logic for checking result of web call and distributing received data omitted
}

我觉得应该为这种事情建立一些完善的模式,确保在选择新的UI项目时取消每个请求,无论用户选择多快。

事实上,这不是一个突出的反应性扩展用例吗?我看到很多提到假设的问题,例如在文本框中输入击键后调用搜索Web服务,但我没有找到任何处理取消先前发送的请求的示例。

我只需要一些干净而且坚如磐石的东西,希望能够打包并隐藏复杂性的东西,以便这种可取消的异步提取可以在我的应用程序的其他地方无故障地使用。

1 个答案:

答案 0 :(得分:3)

您的问题是finally正在修改_customerProfileCtsnull可以Cancel出现其他来电仍在使用的实例。如果你将它移到GetCustomerProfile之后,它应该可以正常工作。实际上,您可以将其与CancellationTokenSource _customerProfileCts; private async void TriggerGetCustomerProfile() { if (_customerProfileCts != null) { _customerProfileCts.Cancel(); } _customerProfileCts = new CancellationTokenSource(); var token = _customerProfileCts.Token; try { IsBusyFetchingCustomerProfile = true; IsCustomerProfileReady = false; await GetCustomerProfile(token); } finally { IsBusyFetchingCustomerProfile = false; } } private async Task GetCustomerProfile(CancellationToken token) { await _customerSvc.GetCustomerProfileReport(SelectedCustomer.Id, token); } 中的修改结合起来:

double l_Value=94.9;
print("%.20lf",l_Value);