为什么控制台版本生成的项目project.assets.json与VS IDE版本生成的项目完全不同?

问题描述

我们有大约100个项目的大型解决方案,这些项目取决于许多NuGet软件包,其中一些是内部开发的。这个问题是关于作为解决方案中心的主要Web应用程序的。

我们想考虑迁移到新的SDK样式的项目(在可行的情况下),第一步是将所有项目迁移到packagereference。这是一个大麻烦事-代码建立时

  • 在带有msbuild的控制台上,我们在bin文件夹中获得System.Buffers版本4.0.3.0。
  • 在带有devenv /build的控制台上,我们在bin文件夹中获得System.Buffers版本4.0.3.0。
  • 在VS IDE(我使用16.7.7)中,我们在bin文件夹中获得System.Buffers 4.0.2.0版( !!! )。

我已经确定差异是由为相关Web应用程序生成的project.assets.json差异引起的。

差异很大,但是绝对的大多数差异是控制台构建的package.assets.json包含的内容比IDE构建的内容多得多。然后是System.Buffers:

enter image description here

注意:

  • 没有项目明确引用System.Buffers
  • 我们有一个流程来确保所有项目都引用特定NuGet软件包的完全相同版本。不遵守将使PR构建失败。但是,这对这些NuGet包的传递依赖关系没有帮助。因此,我们不得不处理程序集绑定重定向(即使我们所有的项目都是未签名的-没关系,许多依赖项也已签名)
  • 内部版本之间未更改任何代码,仅删除了相应Web应用程序的bin文件

我的问题-有人遇到过吗? devenv /build产生与msbuild完全相同的 project.assets.json 文件的事实使我感到惊讶。

我的理论-与VS所采用的快速最新启发式技术有关。但是我不想禁用它-这可以节省大量时间。只是一个理论,一个薄弱的理论,因为毕竟代码没有改变。

我的方法是运行以下脚本:

$FileItem = Get-Item .\bin\_PublishedWebsites\MyWebApp\bin\System.Buffers.dll

del $FileItem.Directory.FullName -r -Force
r.ps1 -nopull -Main -NovalidateSolutions
[reflection.assemblyname]::GetAssemblyName($FileItem.FullName)
copy-Item .\UI\MyWORKBits\obj\project.assets.json c:\temp\_msbuild.project.assets.json

del $FileItem.Directory.FullName -r -Force
devenv Main.sln /build
[reflection.assemblyname]::GetAssemblyName($FileItem.FullName)
copy-Item .\UI\MyWORKBits\obj\project.assets.json c:\temp\_devenv.project.assets.json

del $FileItem.Directory.FullName -r -Force
$VSInstanceFinder = (Get-ToolFromNuGet VSInstanceFinder 1.0.20009.1).FullName
Add-Type -Path $VSInstanceFinder
$dte = [VSInstanceFinder2.Program]::Find((Get-Item .\Main.sln).FullName)
$dte.solution.solutionBuild.Build()
while (!(Test-Path $FileItem.FullName))
{
    Write-Host -NoNewline '.'
    Start-Sleep 10
}
[reflection.assemblyname]::GetAssemblyName($FileItem.FullName)
copy-Item .\UI\MyWORKBits\obj\project.assets.json c:\temp\_ide.project.assets.json
bc c:\temp\_console.project.assets.json c:\temp\_devenv.project.assets.json
bc c:\temp\_console.project.assets.json c:\temp\_ide.project.assets.json

最低复制量

我仍在进行最小限度的复制,以全面展示该问题。但是,为了证明project.assets.json是不同的,尽管以一种无辜的方式,这足以说明一个小项目。

Common.csproj

<?xml version="1.0" encoding="utf-8"?>
<Project Toolsversion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsversion)\Microsoft.Common.props" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">Anycpu</Platform>
    <ProductVersion>8.0.30703</ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{04455D86-A7A4-41E5-B3ED-B0BC65EAFDFD}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AssemblyName>xyz.Common</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|Anycpu' ">
    <OutputPath>Bin\Debug\</OutputPath>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|Anycpu' ">
    <OutputPath>Bin\Release\</OutputPath>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Dummy.cs" />
  </ItemGroup>
  <ItemGroup>
    <packagereference Include="Antlr">
      <Version>3.5.0.2</Version>
    </packagereference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

dummy.cs

using Antlr;

让我们在VS IDE中进行构建,并检查project.assets.json文件

C:\work\vsbug [master]> dir .\obj\project.assets.json | sls projectName

obj\project.assets.json:76:      "projectName": "Common",C:\work\vsbug [master]>

因此projectName被报告为 Common 。现在,让我们在控制台上构建:

C:\work\vsbug [master]> msbuild /restore .\Common.csproj /v:q
Microsoft (R) Build Engine version 16.7.0+b89cb5fde for .NET Framework
copyright (C) Microsoft Corporation. All rights reserved.

C:\work\vsbug [master]> dir .\obj\project.assets.json | sls projectName

obj\project.assets.json:76:      "projectName": "xyz.Common",C:\work\vsbug [master]>

这次,项目名称 xyz.Common

就我而言,这是一个良性差异。但这不应该放在首位,并且可能是我们采用大型解决方案时遇到的问题的症状。

请注意,使用SDK样式项目可以消除差异,但是我不能在大型解决方案中使用它。

devenv /safemode似乎根本没有还原任何NuGet软件包,因此代码无法编译,我无法弄清楚如何使其工作。目前完全没用。

我将尝试使用不同的套件来产生最小的复制品,但是我已经有一个问题-为什么项目名称不同?

编辑1

一个建议可以从预构建步骤开始运行msbuild.exe -t:restore。由于我的解决方案有127个C#项目(加上一个实用程序项目),因此修改每个项目都是不合理的。相反,我将以下目标添加Directory.Build.targets

<Project>
  <Target Name="MSbuildrestore" BeforeTargets="BeforeBuild" Condition="'$(BuildingInsideVisualStudio)' == True">
    <Exec Command="&quot;$(MSBuildBinPath)\msbuild.exe&quot; /t:Restore /v:q /nologo /m $(MSBuildProjectFullPath)" />
  </Target>
</Project>

我仍然必须评估此更改对构建的影响。毕竟,此命令递归到该项目所依赖的每个项目。每个项目都将调用它。

幸运的是,似乎没有破坏Visual Studio的“快速更新”启发式技术,因此我将对其进行一些研究,看看它是否没有意外的惊喜。

编辑2

不幸的是,从预构建步骤生成project.assets.json并不是解决方案。显然,此文件用作构建的输入。下面的简单场景显示了该行为:

  1. 内置msbuild
  2. 设置disableFastUpToDateCheck = true以禁用“快速更新”启发式
  3. 打开VS
  4. 使用二进制日志记录在VS中进行构建(使用Project System Tools生成日志)

检查日志可以发现实际上是在重新编译项目,因为project.assets.json文件是在每次构建之前重新生成的。

不好。

解决方法

研究

这似乎是 VS IDE还原 msbuild还原之间的问题,我在许多项目和PCS中也遇到了这个问题。所以到目前为止,这是一个相当大的问题,感谢您指出这个问题。

=============================== VS IDE还原上的

projects.assert.json

enter image description here

===============================

projects.assert.json关于msbuild还原:

enter image description here

我已经举报了this issue on our DC Forum,如果我没有详细描述该问题,您可以对其进行投票并添加任何评论,以便它得到 Microsoft 的更多关注。我希望团队能给您满意的答复。

建议

由于,该过程可能需要一段时间,建议以消除差异,因此您可以尝试以下功能:

您只需在 VS IDE 中的项目的预构建事件上添加命令行:

1)右键单击您的项目属性-> 构建事件->在 pre-构建事件命令行

msbuild $(MSBuildProjectFullPath) -t:restore

2),然后删除binobj文件夹,在 VS IDE 中重建项目。

前提是您应该将C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe复制到系统环境变量 PATH

如果我的建议不能使您满意,您可以忽略它。