如何包装Web Api';s异步调用,并将结果作为同步方法

本文关键字:调用 异步 结果 同步方法 包装 何包装 Web Api | 更新日期: 2023-09-27 17:59:57

我需要将第三方的Web API方法集成到WCF服务中。WCF服务将由另一个外部方调用,他们希望调用和返回是同步的。

我能够用通常的RestClient.ExecuteAsync从API获得结果。

我为同步调用整理了以下内容:

public static List<Books> GetSyncBooks(int companyId)
{
    var response = GetSynchronousBooks(companyId);  
    var content = response.Result.Content;
    List<Books> result = new List<Books>();
    return Helpers.JSONSerialiser.Deserialize<BookList>(content);
}

async private static Task<IRestResponse> GetSynchronousBooks(int companyId)
{
    var request = BuildGETRequest("Book", companyId);
    var response = await RestSharpHelper.ExecuteSynchronousRequest(request);
    return response;
}
public static Task<IRestResponse> ExecuteSynchronousRequest(RestRequest request)
{
    var client = new RestClient(BaseUrl);
    client.AddHandler("application/json", new RestSharpJsonDotNetDeserializers());
    var tcs = new TaskCompletionSource<IRestResponse>(TaskCreationOptions.AttachedToParent);
    client.ExecuteAsync(request, (restResponse, asyncHandle) =>
    {                
        if (restResponse.ResponseStatus == ResponseStatus.Error)
            tcs.SetException(restResponse.ErrorException);
        else
            tcs.SetResult(restResponse);
    });
    return tcs.Task;
// BREAKPOINT here shows TASK value as
//  Id = 1, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
}

然而,问题是,我从来没有得到一个结果使用这个。响应内容始终为null。我做错了什么?

编辑:谢谢斯蒂芬。我在这里的一些问题上看到了你的名字:你的分数似乎表明你知道如何解决这个问题。根据您对另一个问题的回答,我确实实现了您所说的wcf服务调用。我可以问你一个相关的后续问题吗?像本例这样的异步WCF服务调用的可扩展性如何?它会"只适用于"每秒10到100次的多个同时调用,而忽略下游的处理开销吗?

如何包装Web Api';s异步调用,并将结果作为同步方法

我假设您的WCF服务托管在ASP.NET中。

您的问题在这里:response.Result。我在博客上解释了这种僵局的情况。总之,await将捕获当前的"上下文"(在本例中为ASP.NET请求上下文),并将使用它来恢复async方法。但是,ASP.NET请求上下文一次只允许一个线程,因此,如果请求线程被阻止(调用response.Result),则async方法永远无法继续,并且会出现死锁。

解决方案是纠正这种误解:

WCF服务将由另一个外部方调用,他们希望调用和返回是同步的。

由于您处理的是客户端/服务器场景,因此不必使其同步。客户端的异步性完全独立于服务器的异步性。

因此,只需异步实现WCF服务:

public static Task<List<Books>> GetBooksAsync(int companyId)
{
  var response = await GetBooksAsync(companyId);  
  var content = response.Content;
  List<Books> result = new List<Books>();
  return Helpers.JSONSerialiser.Deserialize<BookList>(content);
}

如果客户愿意,他们仍然可以同步调用它。