在 .NET Core(.NET Standard 1.4 和 .NET Framework 4.6.1)中使用 await/async for System.Net.Http 时出现错误?
Bug when using await/async for System.Net.Http with .NET Core (.NET Standard 1.4 and .NET Framework 4.6.1)?
前景:我正在调查 ASP.NET 核心项目(针对 .NET 4.6.1,使用 .Net Standard 1.4 中的程序集)中的一个错误,该错误引发了 MissingMethodException,尽管这绝不可能是可能的。隔离故障方法,我将代码的核心取出来进行单独的单元测试,该单元测试也针对 .NET 4.6.1。
我从单元测试中收到的异常 ThreadAbortException
或多或少是这样的(有时会有所不同):
at System.Net.Http.WinHttpHandler.SetRequestHandleDecompressionOptions(SafeWinHttpHandle requestHandle)
at System.Net.Http.WinHttpHandler.SetRequestHandleOptions(WinHttpRequestState state)
at System.Net.Http.WinHttpHandler.<StartRequest>d__103.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Net.Http.HttpClient.<FinishSendAsync>d__58.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Hub.Test.UnitTest1.<FetchUrlAsync>d__2.MoveNext()
上述异常的相关代码:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
//FetchUrl(); // works
var task = FetchUrlAsync(); // does not work
}
private void FetchUrl()
{
var handler = new HttpClientHandler();
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
using (var client = new HttpClient(handler, true))
{
var response = client.SendAsync(new HttpRequestMessage(HttpMethod.Get, new Uri("https://github.com/dotnet/core"))).Result;
Trace.WriteLine("FetchUrl:" + response.StatusCode);
}
}
private async Task FetchUrlAsync()
{
try
{
var handler = new HttpClientHandler();
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
using (var client = new HttpClient(handler, true))
{
var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, new Uri("https://github.com/dotnet/core")));
Trace.WriteLine("FetchUrlAsync: " + response.StatusCode);
}
}
catch (Exception e)
{
Trace.WriteLine(e);
throw;
}
}
}
注意,如果我调用 FetchUrl(),代码就可以工作。是的,我知道这在我调用结果的那一刻就变成了同步方法,但它仅用于演示目的。
但是,错误方法 FetchUrlAsync() 不断抛出 ThreadAbortException。
SendAsync 的原因是,原始实现是一个通用实现,对所有 Http* 工作都使用这种方法。使用 GetAsync 等可以触发相同的错误。
我不知道我在这里做错了什么,但我怀疑这个错误是 .NET CLR 中某处的错误。
更新
虽然单元测试问题已解决(谢谢),但我在 ASP.NET Core web-api 中仍然遇到问题。据我了解,.NET Standard 1.4 和 .NET Framework 4.6.1 应该是 1-1 兼容的。这就是为什么我认为框架中存在一个罕见的错误,尽管这似乎不太可能。
共同点是System.Net.Http;这与 .NET Standard 1.4 和 .NET Framework 4.6.1 按位不同;我倾向于找出问题所在,并 post 为可能遇到类似问题的其他人提供答案。
测试方法不等待异步方法完成,因此测试在 SendAsync
有机会 return 之前结束。
您没有指定您使用的测试框架,但我假设它支持异步测试。在这种情况下,将您的测试方法更改为:
[TestMethod]
public async Task TestMethod1()
{
await FetchUrlAsync();
}
如果没有,您将不得不使用 Wait()
:
[TestMethod]
public void TestMethod1()
{
FetchUrlAsync().Wait();
}
好的,解决原来的问题,这是一个 MissingMethodException
由其中一名管制员报告的问题。
此错误仅在使用 async/await 组合时触发,这是有道理的,因为编译器在后台生成了代码。
事实证明,其中一个 Nuget 程序集正在使用 .NET Standard Library 1.6.1
中的 System.Net.Http
,并且此程序集包针对 .NET Standard 1.4(理论上,它与 .NET Framework 4.6.1
).
然后我与解决方案混合,并能够产生 FileNotFoundException: Could not load file or assembly 'System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies.
到目前为止一切顺利。
查看 bin/debug 文件夹时问题变得很明显。找不到 System.Net.Http.dll,因为它正在使用 built-in 框架程序集。
啊哈。
VISUAL STUDIO 2015
查看解决方案,仅在 project.lock.json:
中引用了 built-in 框架程序集
"frameworkAssemblies": [
"System.Net.Http"
]
此引用来自 Microsoft.AspNetCore.Authentication/1.1.0
和 Microsoft.IdentityModel.Protocols/2.1.0
。
删除这两个 frameworkAssemblies
条目使解决方案有效,因为 "real" System.Net.Http.dll 现在已复制到 bin/debug 文件夹。
VISUAL STUDIO 2017
几乎相同的问题;但由于 Microsoft 已弃用 project.json,这里的解决方案是修改(或添加)具有以下内容的 app.config 文件:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
对不起大家这么久post;但是有一个错误 - 它可能不在框架中.. 证据可能指向 msbuild and/or 不同的 IDE。我很高兴我发现了错误并能够更正它。
我希望这篇很长的文章可以帮助其他有类似问题的人。
编码愉快!
前景:我正在调查 ASP.NET 核心项目(针对 .NET 4.6.1,使用 .Net Standard 1.4 中的程序集)中的一个错误,该错误引发了 MissingMethodException,尽管这绝不可能是可能的。隔离故障方法,我将代码的核心取出来进行单独的单元测试,该单元测试也针对 .NET 4.6.1。
我从单元测试中收到的异常 ThreadAbortException
或多或少是这样的(有时会有所不同):
at System.Net.Http.WinHttpHandler.SetRequestHandleDecompressionOptions(SafeWinHttpHandle requestHandle)
at System.Net.Http.WinHttpHandler.SetRequestHandleOptions(WinHttpRequestState state)
at System.Net.Http.WinHttpHandler.<StartRequest>d__103.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Net.Http.HttpClient.<FinishSendAsync>d__58.MoveNext()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Hub.Test.UnitTest1.<FetchUrlAsync>d__2.MoveNext()
上述异常的相关代码:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
//FetchUrl(); // works
var task = FetchUrlAsync(); // does not work
}
private void FetchUrl()
{
var handler = new HttpClientHandler();
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
using (var client = new HttpClient(handler, true))
{
var response = client.SendAsync(new HttpRequestMessage(HttpMethod.Get, new Uri("https://github.com/dotnet/core"))).Result;
Trace.WriteLine("FetchUrl:" + response.StatusCode);
}
}
private async Task FetchUrlAsync()
{
try
{
var handler = new HttpClientHandler();
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
using (var client = new HttpClient(handler, true))
{
var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, new Uri("https://github.com/dotnet/core")));
Trace.WriteLine("FetchUrlAsync: " + response.StatusCode);
}
}
catch (Exception e)
{
Trace.WriteLine(e);
throw;
}
}
}
注意,如果我调用 FetchUrl(),代码就可以工作。是的,我知道这在我调用结果的那一刻就变成了同步方法,但它仅用于演示目的。
但是,错误方法 FetchUrlAsync() 不断抛出 ThreadAbortException。
SendAsync 的原因是,原始实现是一个通用实现,对所有 Http* 工作都使用这种方法。使用 GetAsync 等可以触发相同的错误。
我不知道我在这里做错了什么,但我怀疑这个错误是 .NET CLR 中某处的错误。
更新
虽然单元测试问题已解决(谢谢),但我在 ASP.NET Core web-api 中仍然遇到问题。据我了解,.NET Standard 1.4 和 .NET Framework 4.6.1 应该是 1-1 兼容的。这就是为什么我认为框架中存在一个罕见的错误,尽管这似乎不太可能。
共同点是System.Net.Http;这与 .NET Standard 1.4 和 .NET Framework 4.6.1 按位不同;我倾向于找出问题所在,并 post 为可能遇到类似问题的其他人提供答案。
测试方法不等待异步方法完成,因此测试在 SendAsync
有机会 return 之前结束。
您没有指定您使用的测试框架,但我假设它支持异步测试。在这种情况下,将您的测试方法更改为:
[TestMethod]
public async Task TestMethod1()
{
await FetchUrlAsync();
}
如果没有,您将不得不使用 Wait()
:
[TestMethod]
public void TestMethod1()
{
FetchUrlAsync().Wait();
}
好的,解决原来的问题,这是一个 MissingMethodException
由其中一名管制员报告的问题。
此错误仅在使用 async/await 组合时触发,这是有道理的,因为编译器在后台生成了代码。
事实证明,其中一个 Nuget 程序集正在使用 .NET Standard Library 1.6.1
中的 System.Net.Http
,并且此程序集包针对 .NET Standard 1.4(理论上,它与 .NET Framework 4.6.1
).
然后我与解决方案混合,并能够产生 FileNotFoundException: Could not load file or assembly 'System.Net.Http, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies.
到目前为止一切顺利。
查看 bin/debug 文件夹时问题变得很明显。找不到 System.Net.Http.dll,因为它正在使用 built-in 框架程序集。
啊哈。
VISUAL STUDIO 2015
查看解决方案,仅在 project.lock.json:
中引用了 built-in 框架程序集 "frameworkAssemblies": [
"System.Net.Http"
]
此引用来自 Microsoft.AspNetCore.Authentication/1.1.0
和 Microsoft.IdentityModel.Protocols/2.1.0
。
删除这两个 frameworkAssemblies
条目使解决方案有效,因为 "real" System.Net.Http.dll 现在已复制到 bin/debug 文件夹。
VISUAL STUDIO 2017
几乎相同的问题;但由于 Microsoft 已弃用 project.json,这里的解决方案是修改(或添加)具有以下内容的 app.config 文件:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
对不起大家这么久post;但是有一个错误 - 它可能不在框架中.. 证据可能指向 msbuild and/or 不同的 IDE。我很高兴我发现了错误并能够更正它。
我希望这篇很长的文章可以帮助其他有类似问题的人。
编码愉快!