3

假设我有一个 DelegatingHandler 用于记录 api 请求。我想访问请求和响应内容以保存到数据库。

我可以直接使用:

var requestBody = request.Content.ReadAsStringAsync().Result;

我在很多例子中看到。还建议似乎知道他们在说什么的人在这里使用它。顺便说一句,之所以提出这个建议,是因为发帖人最初使用的是 ContinueWith,但遇到了间歇性问题。

在其他地方,这里,作者明确表示不要这样做,因为它会导致死锁,并建议改用 ContinueWith。此信息显然直接来自 ASP.net 团队。

所以我有点困惑。这两种情况在我看来非常相似,因此似乎相互矛盾。

我应该使用哪个?

4

2 回答 2

3

你应该使用await.

正如我在博客中所描述的那样,其中一个问题Result它可能导致死锁。

问题ConfigureAwait在于,默认情况下,它将在 HTTP 请求上下文之外的线程池上执行延续。

您可以使用这两种方法中的任何一种获得有效的解决方案(尽管正如 Youssef 指出的那样,Result性能仍然会次优),但为什么要麻烦呢?await为您做这一切:没有死锁、优化线程以及在 HTTP 请求上下文中恢复。

var requestBody = await request.Content.ReadAsStringAsync();

.NET 4.0 的编辑:首先,我强烈建议升级到 .NET 4.5。ASP.NET 运行时在 .NET 4.5 中得到了增强,可以正确处理Task基于 的async操作。因此,如果您将 WebAPI 安装到 .NET 4.0 项目中,下面的代码可能会或可能不会起作用。

也就是说,如果您想尝试正确使用 old-school ContinueWith,这样的事情应该可以工作:

protected override Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request,
    CancellationToken cancellationToken)
{
  var context = TaskScheduler.FromCurrentSynchronizationContext();
  var tcs = new TaskCompletionSource<HttpResponseMessage>();
  HttpResponseMessage ret;

  try
  {
    ... // logic before you need the context
  }
  catch (Exception ex)
  {
    tcs.TrySetException(ex);
    return tcs.Task;
  }

  request.Content.ReadAsStringAsync().ContinueWith(t =>
  {
    if (t.Exception != null)
    {
      tcs.TrySetException(t.Exception.InnerException);
      return;
    }

    var content = t.Result;
    try
    {
      ... // logic after you have the context
    }
    catch (Exception ex)
    {
      tcs.TrySetException(ex);
    }
    tcs.TrySetResult(ret);
  }, context);
  return tcs.Task;
}

现在很清楚为什么await要好得多……

于 2013-04-04T20:48:48.180 回答
1

调用 .Result 同步阻塞线程,直到任务完成。这不是一件好事,因为线程只是在等待异步操作完成。

如果您使用的是 .NET 4.5,您应该更喜欢使用 ContinueWith 甚至更好的 async 和 await。这是一个很好的资源,可以让您了解更多信息:

http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx

于 2013-04-04T20:02:39.203 回答