我正在使用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首先要复制这个文件。
答案 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 task,SearchPaths
参数定义为:
指定搜索目录或特殊位置以查找磁盘上代表程序集的文件。搜索路径的列出顺序很重要。对于每个程序集,从左到右搜索路径列表。找到代表程序集的文件后,该搜索将停止,并开始搜索下一个程序集。
...并且它定义了一些特殊的常量,包括我们的朋友{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部分。