只有累積,沒有奇蹟

2018年12月27日 星期四

[.NET] 無法載入檔案或組件 'Newtonsoft.Json' 或其相依性的其中之一 (發生例外狀況於 HRESULT: 0x80131040)

問題
接獲同事報案,反應在測試環境出現以下錯誤訊息 無法載入檔案或組件 'Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' 或其相依性的其中之一。 找到的組件資訊清單定義與組件參考不符。 (發生例外狀況於 HRESULT: 0x80131040) ,實際專案底下 bin 資料夾該 dll 是存在的,心想這問題每隔一陣子都會遇到決定不在挑戰大腦記憶體,將這問題解法記錄下來

探索問題
有踩過這雷的人就會很快知道問題,是多個專案參考到的 dll 版本不同造成,其實仔細看錯誤網頁中有提到幾個關鍵字 組件繫結失敗,跟 google 大神請教後使用 Fuslogvw.exe 可以查到相關線索Fuslogvw (Assembly Binding Log Viewer) 能夠查看應用程式在執行時組件繫結的 Log 資訊與紀錄,協助釐清應用程式無法執行的原因是因為佈署到錯誤的資料夾或是版本號無法對應的問題,且隨著 Visual Studio 一起安裝(同捆包概念),這裡簡單介紹如何使用

開啟 Fuslogvw 
Step 1 : 左下角開始 > 輸入 "Prompt"  >  點選 Developer Command Prompt for VS 2017 
( 補充 : 記得要用管理者權限,否則會無法使用相關功能 )
Step 2 : 在 Developer Command Prompt for VS 2017 輸入 Fuslogvw ,按下 Enter
Step 3 : 會跳出 組件繫結紀錄檢視器 (如果無法按就是沒用 admin 權限開啟)

綁定繫結紀錄 
接下來步驟是說明如何透過 Fuslogvw.exe 的設定取得應用程式中 assembly Binding 的紀錄與資訊
Step 1 : 按下設定,設定以下兩個資訊
  • 啟用紀錄 : 預設是停用紀錄,有紀錄失敗的組件繫結與記錄所有的內容,我比較貪心選擇全部
  • 紀錄檔案的位置 : 設定紀錄 Log 檔的位置,我選擇有個暫存好找的位置存放,看個人喜好
Step 2 : 為了要取得組件繫結失敗 log 資訊,我們再重新執行一次應用程式
Step 3 : 執行完應用程式後按下 重新整理,就會發現左邊區塊出現 assembly Binding 的 log 資訊,像是應用程式assembly 以及 Log的時間等資訊,這樣就代表 Fuslogvw.exe 繫結成功


檢視記錄檔 
回到最初的問題錯誤訊息是 Newtonsoft.Json 載入失敗,這時可以透過 Fuslogvw 查看載入該組件的紀錄,點選 Newtonsoft.Json 後按 檢視記錄檔

Newtonsoft.Json 載入失敗訊息如下
*** 組件繫結器記錄項目 (2018/12/27 @ 上午 04:08:15) ***

作業失敗。
繫結結果: hr = 0x80131040。 沒有可用的描述。

組件管理員的載入來源:  C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
正在可執行檔下執行  c:\windows\system32\inetsrv\w3wp.exe
--- 下列是詳細的錯誤記錄。

=== 繫結前狀態資訊 ===
記錄: DisplayName = Newtonsoft.Json
 (Partial)
警告: 提供了組件的部分繫結資訊:
警告: 組件名稱: Newtonsoft.Json | 網域 ID: 2
警告: 如果只提供部分的組件顯示名稱,就會發生部分繫結。 
警告: 這可能會使繫結器載入不正確的組件。
警告: 建議為組件提供完全指定的文字識別,
警告: 該識別是由簡單名稱、版本、文化特性和公開金鑰語彙基元組成。
警告: 如需詳細資訊和這個問題的一般解決方法,請參閱白皮書 http://go.microsoft.com/fwlink/?LinkId=109270。
記錄: Appbase = file:///D:/application folder/
記錄: 初始 PrivatePath = D:\application folder\bin
記錄: 動態基底 = C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\applicationName\f8aec008
記錄: 快取基底 = C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\applicationName\f8aec008
記錄: AppName = 57a93a57
正在呼叫組件 : (Unknown)。
===
記錄: 此繫結在 default 載入內容中開始。
記錄: 正在使用應用程式組態檔: D:\application folder\web.config
記錄: 使用主機組態檔: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet.config
記錄: 從 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config 使用電腦組態檔。
記錄: 目前不會套用原則至參考 (私用、自訂、部分或以位置為主的組件繫結)。
記錄: 正在嘗試從新的 URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/applicationName/f8aec008/57a93a57/Newtonsoft.Json.DLL 下載。
記錄: 正在嘗試從新的 URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/applicationName/f8aec008/57a93a57/Newtonsoft.Json/Newtonsoft.Json.DLL 下載。
記錄: 正在嘗試從新的 URL file:///D:/application folder/bin/Newtonsoft.Json.DLL 下載。
記錄: 已順利下載組件。正在嘗試安裝檔案: D:\application folder\bin\Newtonsoft.Json.dll
記錄: 進入下載快取安裝階段。
記錄: 組態名稱為: Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
記錄: 從應用程式目錄中執行部分指定的組件繫結成功。必須重新套用原則。
記錄: 正在使用應用程式組態檔: D:\application folder\web.config
記錄: 使用主機組態檔: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet.config
記錄: 從 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config 使用電腦組態檔。
記錄: 在應用程式組態檔中找到重新導向: 重新導向 8.0.0.0 至 11.0.0.0。
記錄: 原則後參考: Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
記錄: GAC 查閱失敗。
記錄: 原則後組件參考必須再進行探查。
記錄: 正在嘗試從新的 URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/applicationName/f8aec008/57a93a57/Newtonsoft.Json.DLL 下載。
記錄: 正在嘗試從新的 URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/applicationName/f8aec008/57a93a57/Newtonsoft.Json/Newtonsoft.Json.DLL 下載。
記錄: 正在嘗試從新的 URL file:///D:/application folder/bin/Newtonsoft.Json.DLL 下載。
記錄: 已順利下載組件。正在嘗試安裝檔案: D:\application folder\bin\Newtonsoft.Json.dll
記錄: 進入下載快取安裝階段。
記錄: 組態名稱為: Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
警告: 比較組件名稱發現不符項目: 主要版本
錯誤: 組件參考不符合找到的組件定義。
錯誤: 安裝失敗,hr = 0x80131040。
錯誤: 無法完成組件的安裝 (hr = 0x80131040)。已終止探查。
 
小弟用我淺薄的知識簡單說明一下 Log : 
  • Line 11 : 說明應用程式要繫結的組鍵名稱
  • Line 20-21 : 本次要載入的應用程式路徑,dll folder位置
  • Line 22-23 : IIS 預設會把編譯後的應用程式資料夾存放在 Framwork安裝目錄 \ Temporary ASP.NET Files 目錄下,詳細可以參考 Understanding ASP.NET Dynamic Compilation 
  • Line 35-37 : 找到繫結組件的 dll 位置與版本等資訊
  • Line 38-39 : Web.Config 中可以設定 assemblyBinding 的版本號與關聯提供載入組鍵時參考詳細可以參考 MSDN 
  • Line 44 : GAC 確認失敗Global Assembly Cache 
  • Line 49-51 : 嘗試從應用程式資料夾底下的dll folder 安裝 Newtonsoft.Json 組鍵,這裡抓到版本號是 8.0.3
  • Line 52-53 : 發現版本不同指定的版號是 11.0.3 安裝的卻是 8.0.3
  • Line 54 : 安裝失敗顯示錯誤訊息 HRESULT: 0x80131040
解法
透過 Fuslogvw 分析完案發現場,確認是 dll 版本不同原因造成,在與同事確認專案的架構確實有多個專案參考來參考去的現象Class lib A 與 Class lib B 都依賴於 Class lib C只要 Class lib C 版本不同時,就會發生執行到不相配的 Json 版本的 dll 時,就會造成 runtime exception發生原因了解了接著看如何解決這問題

設定 assemblyBinding
在使用工具查看 Log 時候可以發現繫結過程中,會去參考 Web.Config 的 assemblyBinding 元素定義來加載 dll 內容,加上以下設定,告訴它在載入組件的過程中要綁定某個版本 ( 相關說明請服用 MSDN )

重建專案檔
目的 : 讓 Visual Studio 重啟加載專案檔及相關設定,重新檢查 Package 之間依賴性
設定方式 : 專案檔右鍵 > Clean > 重啟 Visual Studio > 重新 Build 專案 > 設定為 Release > 開始 debug

重裝異常的 Dll 
目的 : 讓異常的 dll 版本一致 
Step 1 : 專案 Reference > 異常 Dll 右鍵 > 屬性 > 顯示版本 
Step 2 : 到有使用異常 dll 的專案 > Manage Nuget Packages > 選擇 dll > Update version
成功訊息
透過以上方式,應用程式就可以正常使用,在使用 Fuslogvw 查看 Newtonsoft.Json 已正常載入,正常載入訊息如下,宣告破案 (握拳

參考
Could not load file or assembly or one of its dependencies
Fuslogvw.exe (Assembly Binding Log Viewer)
<assemblyBinding>元素<執行階段>

0 意見:

張貼留言

Copyright © m@rcus 學習筆記 | Powered by Blogger

Design by Anders Noren | Blogger Theme by NewBloggerThemes.com