Asynchronous 等待的异步任务丢失HttpContext.Current

Asynchronous 等待的异步任务丢失HttpContext.Current,asynchronous,async-await,rhino-mocks,Asynchronous,Async Await,Rhino Mocks,我有一个非常奇怪的情况,关于一个Web应用程序,它生成一个异步Http Post 我们在TFS有两个分支机构。我将代码从一个分支合并到另一个分支,然后发现新分支中的一些集成测试由于System.NullReferenceException而失败。我花时间确保两个分支中的代码是相同的,并且所有引用的DLL也是相同的。一切似乎都一样 因此,我决定调试测试 我们的测试所做的是创建一个模拟IHttpClient对象。我们以这样的方式存根Mock对象:clientMock.PostAsyncWithRes

我有一个非常奇怪的情况,关于一个Web应用程序,它生成一个异步Http Post

我们在TFS有两个分支机构。我将代码从一个分支合并到另一个分支,然后发现新分支中的一些集成测试由于System.NullReferenceException而失败。我花时间确保两个分支中的代码是相同的,并且所有引用的DLL也是相同的。一切似乎都一样

因此,我决定调试测试

我们的测试所做的是创建一个模拟IHttpClient对象。我们以这样的方式存根Mock对象:clientMock.PostAsyncWithResponseMessage(x,y)返回一个新的HttpResponseMessage()对象(我们在该对象上设置了各种属性)

因此,代码如下所示:

using (var response = await client.PostAsyncWithResponseMessage(url, postData).ConfigureAwait(true))
{
    if (response.IsSuccessStatusCode)
    {
        ret.Response = await response.Content.ReadAsStringAsync().ConfigureAwait(true);
        ret.ContentType = response.Content.Headers.ContentType.ToString();
    }

    ret.StatusCode = response.StatusCode.ToInt();
}
一次一行:

await client.PostAsyncWithResponseMessage(url, postData).ConfigureAwait(true))
这看起来像是一个异步方法,但“客户机”是我们的模拟对象,所以它所做的只是支持IHttpClient接口。如果检查线程,则执行此行时ID不会更改

稍后,我们有:

await response.Content.ReadAsStringAsync().ConfigureAwait(true);
现在,当这一行执行时,HttpContext.Current被设置为null-我们的所有上下文都被破坏了。在这个应用程序中,我们不会在任何地方调用ConfigureAwait(false),所以据我所知,我们没有理由丢失上下文

如果我将该行更改为:

response.Content.ReadAsStringAsync().Result;
这当然是阻塞,我们不会失去上下文

所以有两个问题:

  • 为什么await方法().ConfigureAwait(true)会丢失上下文?[当然,如果您还可以提出为什么来自一个TFS分支的相同代码在另一个分支中工作时失败的原因,那么将获得更多分数,但我不希望如此]
  • 我可以看到等待.PostAsyncWithResponseMessage(url,postData)方法的明显好处,因为在其他服务器处理我们的请求时,我们的线程将被阻塞。但是,在检索数据之后,等待ReadAsStringAsync()有什么好处呢?换句话说,是否有充分的理由不使用.Result语法
  • 谢谢

    如果检查线程,则执行此行时ID不会更改

    这并不一定意味着它是同步运行的。大多数单元测试框架提供一个自由的线程上下文(
    SynchronizationContext.Current
    null
    ),并在线程池线程上运行测试。代码完全有可能恰好在同一个线程上恢复(特别是当您只运行一个测试时)

    现在,当这一行执行时,
    HttpContext.Current
    被设置为
    null
    ——我们所有的上下文都被破坏了。在这个应用程序中,我们不会在任何地方调用
    ConfigureAwait(false)
    ,因此据我所知,我们没有理由丢失上下文

    我假设
    SynchronizationContext.Current
    设置为
    null
    ,因此实际上没有上下文。可能发生的情况是,测试正在设置
    HttpContext.Current
    (在某个随机线程池线程上),并且在第一次真正的异步操作之后,有时测试会在设置了
    Current
    的线程上恢复,有时则不会

    在ASP.NET上,(
    SynchronizationContext
    )处理
    HttpContext.Current
    的传播。如果在单元测试中没有类似的内容,那么就没有上下文可以保留
    HttpContext.Current

    如果您正在寻找快速解决方案,那么您可能可以使用我的。这将提供一个单线程上下文,以便所有异步代码将在同一个线程上恢复-与ASP.NET上下文的语义不同,但相似程度足以用于某些测试:

    void TestMethod()
    {
      AsyncContext.Run(async () =>
      {
        // Test method body goes here.
      });
    }
    
    另一方面,
    ConfigureAwait(true)
    只是噪音
    true
    是默认行为

    是否有充分的理由不使用
    .Result
    语法

    从代码中不清楚服务器的响应是否在
    PostAsyncWithResponseMessage
    完成时被实际读取。根据对问题的描述(以及
    Result
    在测试中修复了问题的事实),听起来
    ReadAsStringAsync
    实际上是异步运行的

    如果是这种情况,那么有两个很好的理由不阻止它:1)你不必要地阻止了一个线程,2)你对自己敞开了大门

    如果检查线程,则执行此行时ID不会更改

    这并不一定意味着它是同步运行的。大多数单元测试框架提供一个自由的线程上下文(
    SynchronizationContext.Current
    null
    ),并在线程池线程上运行测试。代码完全有可能恰好在同一个线程上恢复(特别是当您只运行一个测试时)

    现在,当这一行执行时,
    HttpContext.Current
    被设置为
    null
    ——我们所有的上下文都被破坏了。在这个应用程序中,我们不会在任何地方调用
    ConfigureAwait(false)
    ,因此据我所知,我们没有理由丢失上下文

    我假设
    SynchronizationContext.Current
    设置为
    null
    ,因此实际上没有上下文。可能发生的情况是,测试正在设置
    HttpContext.Current
    (在某个随机线程池线程上),并且在第一次真正的异步操作之后,有时测试会在设置了
    Current
    的线程上恢复,有时则不会

    在ASP.NET上,(
    SynchronizationContext
    )处理
    HttpContext.Current
    的传播。如果在单元测试中没有类似的内容,那么就没有上下文可以保留
    HttpContext.Current

    如果您正在寻找快速解决方案,那么您可能可以使用我的。这将提供一个单线程上下文,以便所有异步代码将在同一个线程上恢复