.net-core - VS 2017 如何构建和测试包含 .Net Core 的混合解决方案

标签 .net-core visual-studio-2017 xunit.net asp.net-core-webapi

我有一个高于平均水平的复杂(我怀疑)解决方案,在升级到 dotnet sdk 1.1 和 Visual Studio 2017 后,我无法从命令行构建和测试。

它一直在 VS 2015 中运行,使用 dotnet sdk 1.0.0-preview2-003131,因此它可以在 VS 2015 和我们构建服务器上的命令行中构建和运行。

但是升级到VS 2017后遇到了一些问题。

概述设置。
我有一个大致如下所示的解决方案(现实生活中的更多项目)

我的解决方案.sln

  • FoundationClasses(x86、.Net Framework 4.5、csproj(旧版)
  • BusinessLogic(x86、.Net Framework 4.5、csproj(legcay)
  • 引用基础类
  • WebApi (Dotnet Core WebApi, net451, x86 (runtime (win8-x86, win10-x86))
  • 测试项目(Dotnet Core、net451、x86)
  • 引用 WebApi

  • 在 VS 2015 中,这是通过在 Git 中存储 restore.dg 和 project.fragment.lock.json 来实现的,然后我可以运行 dotnet restore,随后运行 dotnet build 和 dotnet test。

    升级到 VS 2017 后,当我从 Visual Studio 构建和运行时,一切正常。 (迁移对基础项目的引用有一些问题 - 但删除了这些,并在迁移后重新阅读,然后一切正常)

    'dotnet restore mySolution.sln' 工作正常。它正确地恢复了 WebApi 和 TestProject 的包 - 并且开箱即用,与预览位相反。在那里我不得不摆弄恢复和碎片文件。

    但是,如果我运行 'dotnet build MySolution.sln -f net452 -r win10-x86',我会收到一堆构建错误。

    如果我运行 'dotnet msbuild MySolution.sln -f net452 -r win10-x86' 它可以工作。

    这是在概述的解决方案中从 CLI 工具构建的正确方法吗?

    因此,对于构建和恢复,我可以在 CLI 和 VS 2017 中使用它。并且结果相同。

    但是对于测试,一致性停止了。

    我可以在 Visual Studio 测试资源管理器中运行测试就好了。单元测试运行良好并且是绿色的。但是我启动 TestServer 的集成测试因 Microsoft.Extensions.DependencyInjection.Abstractions 1.0.0 和 1.1.0 的引用不匹配而失败。

    所以测试链中的某些东西需要 1.0.0 的程序集,但在调试目录中只找到 1.1.0。

    这可以通过程序集重定向来解决 - 但相当多的程序集似乎是错误/不匹配的。

    如果我运行 'dotnet test --no-build TestProject/TestProject.csproj' 测试都是绿色的 - 没有问题。

    如此突出的问题:
  • “dotnet msbuild”是构建混合解决方案的正确方法吗?
  • 如果我在没有 --no-build 的情况下运行“dotnet test”,它会在编译时失败 - 与“dotnet build”(不是 msbuild)类似的错误
  • 我的测试中基于哪些不一致 - 我如何在 VS 中执行与 CLI 相同的操作(最好弄清楚 VS 2017 中需要 1.0.0 范围内的程序集的内容)

  • 我希望我能够彻底解释(但又足够简单易懂)。

    如果需要更多信息来了解场景,请告诉我。

    此致
    安德斯

    最佳答案

    好的,经过一些艰苦的思考/挖掘时间,我想我找到了正确的解决方案。

    将在这里发布以供引用。

    对 dotnet cli 问题的交叉引用:https://github.com/dotnet/cli/issues/6032

    我有一个构建到 .Net 4.5、x86 的现有遗留解决方案,我们称之为 oldsolution.sln

    我有一个新的解决方案,其中包含来自 oldsolution.sln 的一些项目和一些新的 dotnet 核心项目,我们称之为 mySolution.sln。

    新的解决方案还需要为 .Net 4.5 构建到 x86

    大厦

    这里要掌握的关键是,Visual Studio 2017 使用安装文件夹中的 msbuild.exe 进行构建。 dotnet msbuild 使用来自 dotnet sdk 文件夹的程序集。而这些并不完全相同。

    因此,要像 Visual Studio 一样构建,我必须找到要使用的正确 msbuild 可执行文件。

    Visual Studio 团队为此开发了一个工具 ( https://www.nuget.org/packages/vswhere ),我在构建脚本中使用了该工具。

    有了这个,一切正常。 ( build 明智)

    下面是我用来构建和测试解决方案的 PSake 脚本。

    Task BuildApi {
        exec { msbuild ./oldSolution.sln /t:Rebuild /p:Configuration="Release" /p:Platform=x86 /m /verbosity:minimal /nr:false }
    
        exec { dotnet restore .\mySolution.sln }
    
        #Find location of VS2017
        $VsPath = .\packages\vswhere.1.0.50\tools\vswhere.exe -latest -property installationPath
        $msBuild17 = "$vsPath\MSBuild\15.0\Bin\MSBuild.exe"
    
        exec { &$msbuild17 ./mySolution.sln /t:Build /p:Configuration=Release /p:Platform=x86 /m /verbosity:minimal /nr:false }
    }
    
    Task TestApi -depends BuildApi{
        if(!(Test-Path TestResults))
        {
        mkdir TestResults
        }
        if(!(Test-Path TestResults/coverage))
        {
        mkdir TestResults/coverage
        }
    
        $coverage = './packages/OpenCover.4.6.519/tools/OpenCover.Console.exe'
        $target = "`"C:\Program Files (x86)\dotnet\dotnet.exe`""
        $filter = "`"+[WebApi]*`""
    
        #UnitTests
        $targetargs = "`"test --no-build .\WebApi\test\WebApi.UnitTests\WebApi.UnitTests.csproj -c Release  --logger `"trx;LogFileName=UnitTests.trx`"`""
        $output = 'TestResults/coverage/WebApi.UnitTests.Coverage.xml'
        &$coverage -register:user -oldstyle -target:$target -targetargs:$targetargs -output:$output -filter:$filter
    
        # IntegrationTests
        $targetargs = "`"test --no-build .\Web\test\WebApi.IntegrationTests\WebApi.IntegrationTests.csproj c Release --logger `"trx;LogFileName=IntegrationTests.trx`"`""
        $output = 'TestResults/coverage/WebApi.IntegrationTests.Coverage.xml'
        &$coverage -register:user -oldstyle -target:$target -targetargs:$targetargs -output:$output -filter:$filter
    
        #Generate HTML report
        $reportGenerator = "./packages/ReportGenerator.2.4.5.0/tools/ReportGenerator.exe"
        $reportFiles = "TestResults/coverage/WebApi.UnitTests.Coverage.xml;TestResults/coverage/WebApi.IntegrationTests.Coverage.xml"
        $targetDir = "./TestResults/coverage/WebApi"
        &$reportGenerator -reports:$reportFiles -targetdir:$targetDir
    }
    

    到现在为止还挺好。

    测试

    然后我在让 OpenCover 获取覆盖率结果时遇到了麻烦。

    发现 XUnit 做了一个 shadowCopy - 解决这个问题是在每个测试项目中放置一个 xunit.runner.json ( https://xunit.github.io/docs/configuring-with-json.html )
    {
      "shadowCopy": false
    }
    

    告诉 XUnit 不要进行卷影复制,因此 OpenCover 可以找到与被测可执行文件匹配的 PDB 文件。

    最后...

    从 Visual Studio 中运行测试,所有使用 Microsoft.AspNetCore.TestHost.TestServer 引用某些 dotnet 核心程序集的测试都失败了,因为“某事”引用了 v 1.0.0 版本的程序集 - 而我的项目引用了 v 1.1.x。

    我通过在 app.config 文件中为所有失败的程序集进行程序集重定向来解决这个问题。无法弄清楚谁/什么使用了旧的 1.0.0 程序集 - 但 VS 2017 构建链中的某些东西似乎可以做到这一点 - 因为它适用于 dotnet 测试。

    但是,这是我的 app.config 的副本,重定向放置在我的集成测试项目中。
    <?xml version="1.0" encoding="utf-8"?>
    
    <configuration>
      <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">"
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
          </dependentAssembly>
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.AspNetCore.Mvc.Core" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.2.0" newVersion="1.1.2.0" />
          </dependentAssembly>
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.Extensions.Options" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.1.0" />
          </dependentAssembly>
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.AspNetCore.Http.Abstractions" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.1.0" />
          </dependentAssembly>
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.AspNetCore.StaticFiles" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.1.0" />
          </dependentAssembly>
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.Extensions.FileProviders.Abstractions" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
          </dependentAssembly>
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.Extensions.Primitives" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
          </dependentAssembly>
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.AspNetCore.Routing" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.1.0" />
          </dependentAssembly>
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.AspNetCore.Routing.Abstractions" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.1.0" />
          </dependentAssembly>
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.AspNetCore.Mvc.Formatters.Json" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.2.0" newVersion="1.1.2.0" />
          </dependentAssembly>
          <dependentAssembly>
            <assemblyIdentity name="Microsoft.AspNetCore.Mvc.ApiExplorer" culture="neutral" publicKeyToken="adb9793829ddae60" />
            <bindingRedirect oldVersion="0.0.0.0-1.1.2.0" newVersion="1.1.2.0" />
          </dependentAssembly>
        </assemblyBinding>
      </runtime>
    </configuration>
    

    呼……那是一场艰苦的战斗。

    但对于它的值(value)。

    与旧版 x86、net 4,5 程序集的组合解决方案,在全新的 .Net Core Web Api 解决方案中运行 - 我很高兴.. :-)

    关于.net-core - VS 2017 如何构建和测试包含 .Net Core 的混合解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42771869/

    相关文章:

    c# - 无法读取 dotnet 核心中链接的 appsettings.json 文件中的值

    asp.net - Visual Studio 不要打开新的浏览器实例

    amazon-web-services - .Net Core DynamodDB 使用 XUnit 进行单元测试

    c# - 如何设置一个单元测试,如果另一个单元测试失败则跳过该单元测试?

    unit-testing - 让 xUnit 组合参数

    c# - 如何为要在 ASP.net Core 中下载的文件创建可下载文件链接

    c# - 是否可以跟踪类的当前实例数?

    docker - Docker Compose无法启动mongo服务,即使主要服务也依赖于它

    visual-studio-2017 - 如何防止 vc_redist.x##.exe (VS2017) 由于挂起的重启而模棱两可地失败?

    c# - Visual Studio 中无法识别的数据库格式 accdb