MSBuild正在用旧版本替换Newtonsoft.Json.dll

时间:2018-06-01 07:58:33

标签: c# asp.net-web-api msbuild json.net teamcity

我正在使用TeamCity中的MSBuild运行器来构建ASP.net web api并运行单元测试。一切正常,直到我升级到“Microsoft Build Tools 2017 15.7.2”。

msbuild突然从“C:\ Program Files(x86)\ ISS \ Microsoft Web Deploy V3”或“C:\ Program Files \ ISS”复制旧版本的Newtonsoft.Json.dll(版本6.0.4.17603)构建解决方案时,Microsoft Web Deploy V3“到输出文件夹。所有项目都使用NuGet引用9.0.1版本。

在构建运行时监视输出文件夹,我可以看到.dll在6.0.4和9.0.1之间来回切换,直到构建结束,并且6.0.4版本仍然存在。

我找到了this question,当我将Web部署文件夹中的Newtonsoft.Json.dll文件重命名为Newtonsoft.Json_old.dll时,msbuild没有替换我的9.0.1版本,一切正常。< / p>

我已经检查过引用Newtonsoft.Json的所有项目都引用了9.0.1版本并在.csproj文件中使用了正确的Hint-Path。

有谁知道如何解决这个问题?我的解决方案似乎更像是一种解决方法,我想知道为什么msbuild首先要复制这个文件。

3 个答案:

答案 0 :(得分:3)

摘要

MSBuild解析程序集时,它将根据您所安装的内容在一些非常奇怪的目录(包括该Web Deploy文件夹)中进行搜索。基于MSBuild参考,我认为这是遗留行为。您可以通过在项目文件中定义的MSBuild属性来阻止此操作。

在受影响的项目文件中,找到以下行:

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

并将其添加到它下面:

<PropertyGroup>
    <AssemblySearchPaths>$(AssemblySearchPaths.Replace('{AssemblyFolders}', '').Split(';'))</AssemblySearchPaths>
</PropertyGroup>

这将导致MSBuild在解决程序集时不再在有问题的文件夹中查找。


全文

当我们移到Visual Studio 2019时,我的团队遇到了类似的问题。我们的某些项目仍针对.NET Framework 4.0,并且在我们的构建代理上安装了Visual Studio 2019之后,我们开始对那些引用了我们的一些核心库:

  

主引用“ OurCoreLibrary,版本= 3.4.2.0,区域性=中性,PublicKeyToken = xxxxxxxxxxxxxxxx,processorArchitecture = MSIL”无法解析,因为它间接依赖程序集“ Newtonsoft.Json,版本= 9.0.0.0” ,文化=中性,PublicKeyToken = 30ad4fe6b2a6aeed”,它是针对“ .NETFramework,Version = v4.5”框架构建的。此版本高于当前目标框架“ .NETFramework,Version = v4.0”的版本。

将项目切换到目标4.5时问题消失了,但是由于我不会进入这里的原因,我们无法为每个受影响的项目都这样做,所以我决定更深入地研究。

事实证明,您的问题提供了一些有关发生情况的见解。我们所引用的Newtonsoft.Json版本与“ C:\ Program Files(x86)\ ISS \ Microsoft Web Deploy V3”中的版本匹配,并且当我删除该文件时,构建成功。

我们的特定问题是Web Deploy文件夹中的Newtonsoft.Json副本是相同版本(9.0.0.0),但框架错误(4.5而不是4.0),并且由于任何原因解析逻辑都不会检查目标框架,在构建时导致不匹配。更新到VS2019涉及更新Web Deploy,这也将Newtonsoft.Json的副本更新到9.0.0.0,从而导致了冲突。

要了解为什么甚至要从头开始查看该程序集,我将MSBuild项目的生成输出详细程度设置为 Diagnostic ,然后查看发生了什么。搜索有问题的路径表明,在 ResolveAssemblyReferences 任务中,MSBuild正在穿越一些意外的地方来查找匹配项:

1>          For SearchPath "{AssemblyFolders}". (TaskId:9)
1>          Considered "C:\Program Files (x86)\Microsoft.NET\ADOMD.NET\140\OurCoreLibrary.winmd", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Microsoft.NET\ADOMD.NET\140\OurCoreLibrary.dll", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Microsoft.NET\ADOMD.NET\140\OurCoreLibrary.exe", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0\OurCoreLibrary.winmd", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0\OurCoreLibrary.dll", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0\OurCoreLibrary.exe", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\OurCoreLibrary.winmd", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\OurCoreLibrary.dll", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\OurCoreLibrary.exe", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files\IIS\Microsoft Web Deploy V3\OurCoreLibrary.winmd", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files\IIS\Microsoft Web Deploy V3\OurCoreLibrary.dll", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files\IIS\Microsoft Web Deploy V3\OurCoreLibrary.exe", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Microsoft SQL Server\140\SDK\Assemblies\OurCoreLibrary.winmd", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Microsoft SQL Server\140\SDK\Assemblies\OurCoreLibrary.dll", but it didn't exist. (TaskId:9)
1>          Considered "C:\Program Files (x86)\Microsoft SQL Server\140\SDK\Assemblies\OurCoreLibrary.exe", but it didn't exist. (TaskId:9)

进一步的挖掘显示,搜索到的路径以Microsoft.Common.CurrentVersion.targets中定义的AssemblySearchPaths的形式传入:

<AssemblySearchPaths Condition=" '$(AssemblySearchPaths)' == ''">
  {CandidateAssemblyFiles};
  $(ReferencePath);
  {HintPathFromItem};
  {TargetFrameworkDirectory};
  $(AssemblyFoldersConfigFileSearchPath)
  {Registry:$(FrameworkRegistryBase),$(TargetFrameworkVersion),$(AssemblyFoldersSuffix)$(AssemblyFoldersExConditions)};
  {AssemblyFolders};
  {GAC};
  {RawFileName};
  $(OutDir)
</AssemblySearchPaths>

根据MSBuild Task Reference for the ResolveAssemblyReferences taskSearchPaths参数定义为:

  

指定搜索目录或特殊位置以查找磁盘上代表程序集的文件。搜索路径的列出顺序很重要。对于每个程序集,从左到右搜索路径列表。找到代表程序集的文件后,该搜索将停止,并开始搜索下一个程序集。

...并且它定义了一些特殊的常量,包括我们的朋友{AssemblyFolders}

  
      
  • {AssemblyFolders}:指定任务将使用Visual Studio.NET 2003从注册表查找组件。
  •   

由于目录是按顺序检查的,因此您可能希望{HintPathFromItem}优先,在大多数情况下,它会优先。但是,如果您依赖于Newtonsoft.Json的较旧版本,则该版本将没有HintPath ,因此它将一直持续到问题解决。

Later on in Microsoft.Common.CurrentVersion.targets我们可以看到,在某些情况下,该常量被显式删除,这就是上面的答案来自的地方:

<PropertyGroup Condition="'$(_TargetFrameworkDirectories)' == '' and '$(AssemblySearchPaths)' != '' and '$(RemoveAssemblyFoldersIfNoTargetFramework)' == 'true'">
  <AssemblySearchPaths>$(AssemblySearchPaths.Replace('{AssemblyFolders}', '').Split(';'))</AssemblySearchPaths>
</PropertyGroup>

删除此常量将不考虑有问题的文件夹,老实说,我无法想到这样一种情况:我希望程序集隐式解析为Web Deploy或Windows hanging中的任何版本,例如Newtonsoft.Json。 SQL Server SDK文件夹。话虽这么说,我敢肯定有一个案例,将其关闭会导致某些问题,因此请记住这一点。

答案 1 :(得分:1)

触发自定义构建并勾选“在构建之前清除checkout目录中的所有文件”可能是值得的 - 您可能会有相互矛盾的构建工具延迟。

答案 2 :(得分:0)

我找到了另一个简单的解决方案。我手动移动

<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
      <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
    </Reference>

带有参考的最后一个ItemGroup部分。