﻿# 錯誤的 HintPath 也可以在 Visual Studio 中建置，但 CI 的時候可能不行

我在幫一個客戶 Code Review 並重構專案的時候，發現他們的專案在 Visual Studio 2019 都可以順利的建置專案，但是透過 Azure Pipelines 使用 MSBuild 建置卻會編譯失敗，錯誤訊息非常不清楚，真的是查了很久才想通問題的主因在哪裡，我覺得值得寫篇文章記錄一下。

### 錯誤訊息

發生建置失敗時，通常都是先透過 Log 找到蛛絲馬跡，首先映入眼簾的是「錯誤」訊息，總共有 68 個：

```log
##[error]Common\Shared\CheckMod.cs(15,18): Error CS0234: The type or namespace name 'Mvc' does not exist in the namespace 'System.Web' (are you missing an assembly reference?)
##[error]Common\Utils\AzureStorage.cs(8,17): Error CS0234: The type or namespace name 'Azure' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?)
##[error]Common\FTP\FtpUtils.cs(1,7): Error CS0246: The type or namespace name 'FluentFTP' could not be found (are you missing a using directive or an assembly reference?)
...
...
```

這錯誤很怪，我明明 NuGet Restore 都有成功，怎麼可能編譯時找不到命名空間呢？我在本機是可以編譯的啊！

一般來說，錯誤都解決不了，通常不會去找「警示」訊息，但我還是去看了，共有 30 個：

```log
##[warning]C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(2203,5): Warning MSB3245: Could not resolve this reference. Could not locate the assembly "AngleSharp, Version=0.14.0.0, Culture=neutral, PublicKeyToken=e83494dcdc6d31ea, processorArchitecture=MSIL". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.
##[warning]C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(2203,5): Warning MSB3245: Could not resolve this reference. Could not locate the assembly "AngleSharp.Css, Version=0.14.2.0, Culture=neutral, PublicKeyToken=e83494dcdc6d31ea, processorArchitecture=MSIL". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.
##[warning]C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(2203,5): Warning MSB3245: Could not resolve this reference. Could not locate the assembly "AntiXssLibrary, Version=4.3.0.0, Culture=neutral, PublicKeyToken=d127efab8a9c114f, processorArchitecture=MSIL". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.
##[warning]C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(2203,5): Warning MSB3245: Could not resolve this reference. Could not locate the assembly "EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.
...
...
```

他顯示 **Check to make sure the assembly exists on disk** 耶！但我的資料夾明明就有完整的 `packages` 目錄，套件也都有還原成功啊！

魔鬼總是出在細節裡，這段 Log 真的很難看出問題所在。我一開始只看出他「非常認真」的想找到 `EntityFramework.dll` 在哪裡，但最終就是找不到！

```log
2021-09-08T09:59:25.0317073Z C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(2203,5): warning MSB3245: Could not resolve this reference. Could not locate the assembly "EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL". Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors. [D:\a\1\s\Common.csproj]
2021-09-08T09:59:25.0319856Z    For SearchPath "{HintPathFromItem}".
2021-09-08T09:59:25.0320809Z    Considered "D:\a\1\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0321729Z    For SearchPath "{TargetFrameworkDirectory}".
2021-09-08T09:59:25.0322812Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0324318Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0326235Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0327345Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\Facades\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0329704Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\Facades\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0331000Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\Facades\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0332550Z    For SearchPath "{AssemblyFoldersFromConfig:C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\AssemblyFolders.config,v4.6.1}".
2021-09-08T09:59:25.0333890Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0334975Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0336185Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0337230Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\Microsoft\SqlDb\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0338741Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\Microsoft\SqlDb\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0339871Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\Microsoft\SqlDb\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0341008Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\PublicAssemblies\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0342446Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\PublicAssemblies\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0343716Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\PublicAssemblies\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0344976Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.5\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0346011Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.5\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0347336Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.5\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0348385Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VSSDK\VisualStudioIntegration\Common\Assemblies\v4.0\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0349711Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VSSDK\VisualStudioIntegration\Common\Assemblies\v4.0\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0350959Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VSSDK\VisualStudioIntegration\Common\Assemblies\v4.0\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0352430Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\PublicAssemblies\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0353536Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\PublicAssemblies\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0354501Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\PublicAssemblies\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0355937Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.5\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0357043Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.5\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0358358Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.5\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0359726Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.0\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0360920Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.0\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0362581Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.0\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0364118Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.0\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0365506Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.0\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0366461Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v4.0\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0367645Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v2.0\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0368749Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v2.0\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0370328Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v2.0\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0371751Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VSSDK\VisualStudioIntegration\Common\Assemblies\v2.0\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0373197Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VSSDK\VisualStudioIntegration\Common\Assemblies\v2.0\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0374668Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VSSDK\VisualStudioIntegration\Common\Assemblies\v2.0\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0375585Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v2.0\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0376751Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v2.0\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0377919Z    Considered "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\ReferenceAssemblies\v2.0\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0378757Z    For SearchPath "{Registry:Software\Microsoft\.NETFramework,v4.6.1,AssemblyFoldersEx}".
2021-09-08T09:59:25.0379755Z    Considered AssemblyFoldersEx locations.
2021-09-08T09:59:25.0380118Z    For SearchPath "{AssemblyFolders}".
2021-09-08T09:59:25.0380669Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0381945Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0382984Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0384254Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0385219Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0386357Z    Considered "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0387438Z    Considered "C:\Program Files\IIS\Microsoft Web Deploy V3\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0388370Z    Considered "C:\Program Files\IIS\Microsoft Web Deploy V3\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0389534Z    Considered "C:\Program Files\IIS\Microsoft Web Deploy V3\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0390627Z    Considered "C:\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0391822Z    Considered "C:\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0393042Z    Considered "C:\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0394137Z    Considered "C:\Program Files (x86)\WiX Toolset v3.11\SDK\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0395074Z    Considered "C:\Program Files (x86)\WiX Toolset v3.11\SDK\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0395903Z    Considered "C:\Program Files (x86)\WiX Toolset v3.11\SDK\EntityFramework.exe", but it didn't exist.
2021-09-08T09:59:25.0396662Z    For SearchPath "{GAC}".
2021-09-08T09:59:25.0397574Z    Considered "EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL", which was not found in the GAC.
2021-09-08T09:59:25.0398536Z    For SearchPath "{RawFileName}".
2021-09-08T09:59:25.0399682Z    Considered treating "EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL" as a file name, but it didn't exist.
2021-09-08T09:59:25.0400706Z    For SearchPath "bin\Release\".
2021-09-08T09:59:25.0401283Z    Considered "bin\Release\EntityFramework.winmd", but it didn't exist.
2021-09-08T09:59:25.0402284Z    Considered "bin\Release\EntityFramework.dll", but it didn't exist.
2021-09-08T09:59:25.0403161Z    Considered "bin\Release\EntityFramework.exe", but it didn't exist.
```

我比較不能理解的地方是，在我自己的電腦用 MSBuild 是可以成功建置的，但是在 Azure Pipelines 的 Hosted Agent 用 MSBuild 就是找不到編譯所需的**組件**(Assembly)！

### 解決方法

我經常說：「寫 Code 需要靈感，解 Bug 需要創意」，沒靈感的時候還可以上網抄點 Code，但沒創意你就抓不到這種詭異的 Bug 了！

我最後的解決方法，還是靠眼睛看 Log，搭配創意的大腦思考每一段內容的蛛絲馬跡，最後被我找到這個線索：

```log
Considered "D:\a\1\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll", but it didn't exist.
```

看出來了嗎？還沒？我們再看細一點：

```log
Considered "D:\a\1\packages\..."
```

我突然恍然大悟，因為 Azure Pipelines 預設的 Source 路徑 (`Build.SourcesDirectory`) 是在 `D:\a\1\s` 目錄下，當我在專案根目錄執行 `nuget restore` 的時候，照理說 NuGet 的 `packages` 應是在 `D:\a\1\s\packages` 目錄才對，怎麼會變成 `D:\a\1\packages` 目錄呢？

最後我才想到可能是 `*.csproj` 裡面參考的**組件路徑**有誤，打開來一看，發現組件參考的 `HintPath` 路徑真的有錯，當我批次調整錯誤的路徑之後，專案就可以順利的在 CI 自動建置完成了！ 😃

![修正 HintPath 錯誤的路徑](https://stwillblogassets.blob.core.windows.net/files/images/external/user-images.githubusercontent.com/360de635af22c5221786-132785197-b4770d4d-b8f5-40ab-886b-f1605e4d2c51.png)

### 問題主因

其實客戶的專案檔原本是好的，但是我們在合併不同專案的時候，有將幾個專案移動到上一層目錄，但是當下並沒有意識到 `*.csproj` 也要一併修改**組件參考**的相對路徑。由於 .NET Framework 大部分的組件參考都是相對於 `packages` 這個 NuGet 套件快取目錄的路徑，當我的專案往上移動一層時，專案檔中的路徑也要一併修改才行，這是我沒有注意到的地方！ 🔥

這個問題最終未解的疑惑，就是不知道為什麼在我的電腦可以用 Visual Studio 2019 與 MSBuild 成功建置呢？是不是在其他電腦才不行？是否 MSBuild 的版本不同造成的差異？還是 Visual Studio 2019 版本不同造成的？這個問題等我下次遇到相同問題時再來研究了！

### 批次重裝所有 NuGet 套件

> 感謝 [Poy Chang 的技術交流中心](https://www.facebook.com/poychang.tech) 分享以下 PowerShell 指令，這個命令可以在 Visual Studio 2019 的 **Package Manager Console** 底下執行，他會全自動找出所有專案的所有已安裝的 NuGet 套件，並且自動全部重新安裝！ 👍

```powershell
Get-Project -All | % { Get-Package -ProjectName $_.ProjectName | % { Update-Package $_.Id -Reinstall -ProjectName $_.ProjectName -IgnoreDependencies } }
```

在重新安裝完之後，所有的問題都會自動迎刃而解！

### 相關連結

-   [Predefined variables - Azure Pipelines | Microsoft Docs](https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml&WT.mc_id=DT-MVP-4015686)
-   [.net - HintPath vs ReferencePath in Visual Studio - Stack Overflow](https://stackoverflow.com/a/25862811/910074)
-   [Resolving assembly references in Team Build ( Part - 1 )](https://learn.microsoft.com/en-us/archive/blogs/manishagarwal/resolving-assembly-references-in-team-build-part-1?WT.mc_id=DT-MVP-4015686)
-   [Resolving file references in team build ( Part - 2 )](https://learn.microsoft.com/en-us/archive/blogs/manishagarwal/resolving-file-references-in-team-build-part-2?WT.mc_id=DT-MVP-4015686)
-   [跨解決方案引用專案的潛在NuGet路徑問題-黑暗執行緒](https://blog.darkthread.net/blog/nuget-hintpath-across-solutions/)
-   [\[NuGet\] 解決 HintPath 造成的編譯問題 | 胖胖的點人生 - 點部落](https://dotblogs.com.tw/FatJohn/2016/12/23/NuGetHintPath)
