如何将Web Api的异步调用和结果包装为同步方法?

时间:2014-04-25 09:56:29

标签: c# wcf asp.net-web-api restsharp

我需要将第三方的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范围内的多个同时呼叫,它是否“正常工作”,忽略了下游的处理开销?

1 个答案:

答案 0 :(得分:4)

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

您的问题在于:response.Result。我在博客上解释this deadlock situation。总之,await将捕获当前&#34;上下文&#34; (在这种情况下,是一个ASP.NET请求上下文),并将使用它来恢复async方法。但是,ASP.NET请求上下文一次只允许一个线程,因此如果请求线程被阻塞(调用response.Result),那么async方法永远不会继续,并且会出现死锁。 / p>

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

  

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);
}

如果客户希望,客户端仍然可以同步调用它。