Asynchronous 等待的异步任务丢失HttpContext.Current
我有一个非常奇怪的情况,关于一个Web应用程序,它生成一个异步Http Post 我们在TFS有两个分支机构。我将代码从一个分支合并到另一个分支,然后发现新分支中的一些集成测试由于System.NullReferenceException而失败。我花时间确保两个分支中的代码是相同的,并且所有引用的DLL也是相同的。一切似乎都一样 因此,我决定调试测试 我们的测试所做的是创建一个模拟IHttpClient对象。我们以这样的方式存根Mock对象:clientMock.PostAsyncWithResponseMessage(x,y)返回一个新的HttpResponseMessage()对象(我们在该对象上设置了各种属性) 因此,代码如下所示:Asynchronous 等待的异步任务丢失HttpContext.Current,asynchronous,async-await,rhino-mocks,Asynchronous,Async Await,Rhino Mocks,我有一个非常奇怪的情况,关于一个Web应用程序,它生成一个异步Http Post 我们在TFS有两个分支机构。我将代码从一个分支合并到另一个分支,然后发现新分支中的一些集成测试由于System.NullReferenceException而失败。我花时间确保两个分支中的代码是相同的,并且所有引用的DLL也是相同的。一切似乎都一样 因此,我决定调试测试 我们的测试所做的是创建一个模拟IHttpClient对象。我们以这样的方式存根Mock对象:clientMock.PostAsyncWithRes
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;
这当然是阻塞,我们不会失去上下文
所以有两个问题:
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
如果您正在寻找快速解决方案,那么您可能可以使用我的。这将提供一个单线程上下文,以便所有异步代码将在同一个线程上恢复