使用 .NET Core(.NET Standard 1.4 和 .NET Framework 4.6.1)对 System.Net.Http 使用 await/async 时出现错误?

Posted

技术标签:

【中文标题】使用 .NET Core(.NET Standard 1.4 和 .NET Framework 4.6.1)对 System.Net.Http 使用 await/async 时出现错误?【英文标题】:Bug when using await/async for System.Net.Http with .NET Core (.NET Standard 1.4 and .NET Framework 4.6.1)? 【发布时间】:2017-01-17 15:23:08 【问题描述】:

前景:我正在调查一个 ASP.NET Core 项目(针对 .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() 代码可以工作。是的,我知道这在我调用 result 的那一刻就变成了一种同步方法,但这仅用于演示目的。

但是,错误方法 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 不同;我倾向于找出问题所在,并将答案发布给可能遇到类似问题的其他人。

【问题讨论】:

好吧,首先你不是 awaiting 任务。 在一种方法中responseTask&lt;HttpResponseMessage&gt;,另一种方法是HttpResponseMessage,但您似乎对它们都一视同仁。 @juharr 注意非异步方法末尾的.Result 你必须等待 FetchUrlAsync 否则测试将在SendAsync 有机会返回之前退出 Touché,Oleg .. 我一直在盯着这个。我很抱歉。这个“快速”结论的原因是,我发现 .NET Standard 1.4 和 .NET Framework 在通用 API 方面的一些特性。 【参考方案1】:

好的,解决原来的问题,即其中一位控制器报告的MissingMethodException

此错误仅在使用 async/await 组合时触发,这是有道理的,因为编译器会在后台生成代码。

事实证明,其中一个 Nuget 程序集使用了来自 .NET Standard Library 1.6.1System.Net.Http,并且该程序集包针对 .NET Standard 1.4(在纸面上,它与 .NET Framework 4.6.1 1-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,因为它使用的是内置的框架程序集。

啊哈。

视觉工作室 2015

查看解决方案,在project.lock.json中只引用了内置Framework组件:

    "frameworkAssemblies": [
      "System.Net.Http"
    ]

此参考来自Microsoft.AspNetCore.Authentication/1.1.0Microsoft.IdentityModel.Protocols/2.1.0

删除这两个 frameworkAssemblies 条目使解决方案有效,因为“真实” System.Net.Http.dll 现在已复制到 bin/debug 文件夹。

视觉工作室 2017

几乎相同的问题;但由于微软已经弃用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>

我很抱歉给大家发了这么长的帖子;但有一个错误 - 它可能不在框架中.. 证据可能指向 msbuild 和/或不同的 IDE。我很高兴我发现了错误并能够纠正它。

我希望这篇相当长的条目可以帮助其他有类似问题的人。

编码愉快!

【讨论】:

.NET Standard 的更多信息:blogs.msdn.microsoft.com/dotnet/2016/09/26/… 对于 .net Standard 2,使用 【参考方案2】:

测试方法不会等待异步方法完成,因此测试在SendAsync 有机会返回之前结束。

您没有指定您使用的测试框架,但我认为它支持异步测试。在这种情况下,将您的测试方法更改为:

[TestMethod]
public async Task TestMethod1()

    await FetchUrlAsync(); 

如果没有,您将不得不使用Wait()

[TestMethod]
public void TestMethod1()

    FetchUrlAsync().Wait(); 

【讨论】:

以上是关于使用 .NET Core(.NET Standard 1.4 和 .NET Framework 4.6.1)对 System.Net.Http 使用 await/async 时出现错误?的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core (.NET Core) and ASP.NET Core (.NET Framework)区别

细说.net core Startup

.Net Core 学习 - ASP.NET Core 概念学习

NET Standard vs Net Core App:创建 .NET Core 项目时(使用控制台或类库)

.net core系列之《.net core中使用MySql以及Dapper》

2021-06-29 .NET高级班 75-ASP.NET Core Grpc在Core中的使用