The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

如何對 ASP.NET MVC 4 原始碼進行偵錯 (終極完整觀念版)

今天有讀者反映我的書【ASP.NET MVC 4 開發實戰】講到 如何對 ASP.NET MVC 原始碼進行偵錯 這一個技巧時,照著書操作沒有成功,我自己重新試了一次,還真的沒辦法,這才發現事情沒有想像中單純,魔鬼總在細節裡,我今天又花了 6 個小時反覆研究、測試、分析,這才終於釐清為什麼以前可以,現在卻無法對 ASP.NET MVC 4 原始碼進行偵錯的原因。因此我打算轉載書中的內容,並加以更新、補充,好讓大家能夠完整且順利的將 ASP.NET MVC 4 原始碼加入到專案之中。

※本篇文章摘錄自筆者出版的【ASP.NET MVC 4 開發實戰】一書,並且更新部分內容!

開發 ASP.NET MVC 4 的過程中,難免會遇到一些疑難雜症,若能進一步瞭解 ASP.NET MVC 的內部運作邏輯,將有助於瞭解問題背後的運作細節,還好 ASP.NET MVC 是個開放原始碼專案,任何人都可以到 CodePlex 網站下載到目前最新版的 ASP.NET MVC 原始碼,最新版的 ASP.NET MVC 原始碼可以在以下網址取得:http://aspnetwebstack.codeplex.com/

如果需要針對 ASP.NET MVC 4 原始碼一併偵錯,建議可以試試從 CodePlex 下載原始碼,與現有的 ASP.NET MVC 4 專案放在一起,這樣網站在執行時,就能同時查看 ASP.NET MVC 4 的原始碼與執行單步偵錯。

補充說明:筆者在學習 ASP.NET MVC 時,經常查閱 ASP.NET MVC 原始碼,這有助於瞭解並釐清各種 ASP.NET MVC 開發細節。

下載 ASP.NET MVC 4.0 原始碼,並加入專案參考的流程如下:

Step01:前往 http://aspnetwebstack.codeplex.com/ 並點擊 SOURCE CODE 頁籤進入原始碼頁面。

 

Step02:先點選 Browsing changes in 下拉選單,並確認原始碼分支 (Branches) 必須是 master 才行

如下圖示,請千萬不要選擇到非 master 的分支版本,這是我測試數小時得知的結論 Orz

事實上,如果你從 ASP.NET MVC 4.0 RTM 的下載頁面中,也可以看到相對應的原始碼連結(如下圖示),這裡所連結到的原始碼將會是 v2-rtm 分支的原始碼,下載這一份將會遇到許多靈異現象,容後在述!

image

 

補充說明:預設你在 SOURCE CODE 頁面所看到的 ASP.NET MVC 原始碼是 master 分支版本,代表的是持續開發中的原始碼;但是另外還有兩個分支分別是 v2-rcv2-rtm 分支,分別代表著 ASP.NET MVC 4 RCASP.NET MVC 4 RTM官方正式版本原始碼

master 分支版本裡,永遠都是最新版的 ASP.NET MVC 版本,而目前最新版的 ASP.NET MVC 原始碼已經是 ASP.NET 4.5 + ASP.NET MVC 5 (Preview) 的版本囉!(不要懷疑,目前 ASP.NET MVC 5 正在發展中,而且發展中的原始碼大家一樣可以下載預覽)

這也代表著,在 master 分支版本裡的最新版原始碼,已經與 ASP.NET MVC 4 RTM 版本相差勝遠,經一番工夫的研究後發現,差異的地方真的非常多:

  • .NET Framework 版本,從當初的 4.0 已經升級到 4.5
  • ASP.NET MVC 版本從 4.0 升級到 5.0
    • ASP.NET MVC 的組件版本編號,從 4.1.0.0 升級到 5.0.0.0
    • ASP.NET WebPages ( Razor ) 的組件版本編號,從 2.1.0.0 升級到 3.0.0.0
  • Visual Studio 2012 版本推出了 Update 12012.2 更新 (源碼有因為 2012.2 的出現做了些更新)

然而,如果直接照書中所寫的直接下載原始碼,加入專案到最後無法成功建置的最主要原因則是:

  • .NET Framework 版本不相容!

因此,你只要下載到正確 .NET Framework 版本的原始碼,就可以成功加入專案了!

要找到正確的原始碼版本,你必須先點選 SOURCE CODE 頁面下的 History 子頁籤,並確保 Commits in 的下拉選單停留在 master 分支,如下圖示:

然後捲到最下方,選擇每頁顯示 100 筆資料,這樣比較好搜尋

接著按下 Ctrl+F 搜尋頁面資料,關鍵字請輸入:2012.2-rtm

搜尋到之後 ( 如下圖示,點圖可放大顯示 ),你會發現幾個關鍵點,往上兩個版本之後,這個分支就將 ASP.NET MVC 升級到 5.0 版了,也代表著 ASP.NET MVC 5.0 的開始。 ( 如果搜尋不到,請換到下一頁繼續搜尋 )

這時你搜尋到的原始碼版本編號為 1b78397f32fc ( 你也可以直接點選這個連結進入下載頁面 )

 

Step03:選對版本後,就可以點擊右側的 Download 連結,下載完整原始碼。

備註:檔名應該會是 aspnetwebstack-1b78397f32fc.zip

 

Step04:將下載後的 aspnetwebstack-1b78397f32fc.zip 檔案解壓縮到你現有的方案目錄下

 

Step05:將你的現有方案執行 啟用 NuGet 套件還原 功能。

完成後如下圖示:

 

Step06:到 Visual Studio 2012 方案總管,將 ASP.NET MVC 4 相關的原始碼專案加入方案中。

由於 ASP.NET MVC 4.0 原始碼方案中的專案很多,如果你有用 Visual Studio 開啟下載的 ASP.NET MVC 4.0 原始碼方案檔 (Runtime.sln) 的話,就可以看到,除了單元測是的專案之外,有高達 21 個專案之多,如下圖示:

但是,我們如果只需讓現有專案ASP.NET MVC 4 核心程式一起運作就夠了,並不是所有專案都會用到,因此,你只要在現有方案中加入以下 5 個專案,就能夠正確編譯:

  • System.Web.Mvc
  • System.Web.Razor
  • System.Web.WebPages
  • System.Web.WebPages.Deployment
  • System.Web.WebPages.Razor

完成後的圖示如下:

 

Step07:接著修改原本 ASP.NET MVC 專案的組件參考,將準備加入的這幾個組件參考移除。

再重新加入這幾個專案參考。

 

Step08:由於透過 Visual Studio 2012 的 ASP.NET MVC 4 專案範本會加入的組建來自於 NuGet 下載的正式版本,而這些微軟發佈的組件,都會有組件編號以及一個延遲簽署的 Strong Name Key 所附帶的公開金鑰語彙基元 (PublicKeyToken),為了讓這五個 ASP.NET MVC 4 的專案原始碼能夠與目前的專案順利結合,又要避免這幾個專案編譯出來的組件已經註冊在 GAC 中的組件互相不衝突,我們必須對現有的 ASP.NET MVC 專案下的 Views\web.config 組態檔稍作修改。

補充說明:公開金鑰語彙基元 (PublicKeyToken) 是來自於應用程式簽名的公開金鑰,經過 SHA-1 雜湊運算的結果,並取得最後 8 個位元組所表示。

如果沒修改 Views\web.config 組態檔就直接執行網站,執行網站時就會看到如下圖錯誤 (點圖可放大):

[A]System.Web.WebPages.Razor.Configuration.HostSection 無法轉型為 [B]System.Web.WebPages.Razor.Configuration.HostSection. 型別 A 源自 'System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' (在內容 'Default' 中,位置為 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_2.0.0.0__31bf3856ad364e35\System.Web.WebPages.Razor.dll'). 型別 B 源自 'System.Web.WebPages.Razor, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' (在內容 'Default' 中,位置為 'G:\Temp\Temporary ASP.NET Files\root\23e4a2c6\cb39cbc1\assembly\dl3\ceda86bd\0077b1ae_9621ce01\System.Web.WebPages.Razor.dll').

文字錯誤訊息為:

[A]System.Web.WebPages.Razor.Configuration.HostSection 無法轉型為 [B]System.Web.WebPages.Razor.Configuration.HostSection. 型別 A 源自 'System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' (在內容 'Default' 中,位置為 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Web.WebPages.Razor\v4.0_2.0.0.0__31bf3856ad364e35\System.Web.WebPages.Razor.dll'). 型別 B 源自 'System.Web.WebPages.Razor, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' (在內容 'Default' 中,位置為 'G:\Temp\Temporary ASP.NET Files\root\23e4a2c6\cb39cbc1\assembly\dl3\ceda86bd\0077b1ae_9621ce01\System.Web.WebPages.Razor.dll').

代表著兩個相同完整名稱的類別發生衝突,因為 .NET Framework 載入組件的順序是固定的,一定會先載入 GAC 的組件,然後才會嘗試載入 bin 目錄下的組件。所以當載入了兩個同名的組件後,就會遇到這種錯誤,解決方法就是修改 Views\web.config 組態檔。

你只要將檔案中的以下兩段文字,利用「全部取代」的方式,將出現的字串全部清空,就可以修改完成:

, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35
, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35

請注意:我因為測試的時候,有試著下載 v2-rtm 分支的原始碼回來編譯,並加入方案中,結果卻造成一系列靈異現象發生,這也是我今天花最多時間的地方,非常值得一提。

首先,我從 master 分支下載回來的 5 個專案,其設定的組件編號為:

  • System.Web.Mvc 的組件版本編號為 4.1.0.0 ( 註冊在 GAC 的組件版本為 4.0.0.0 )
  • System.Web.Razor 的組件版本編號為 2.1.0.0 ( 註冊在 GAC 的組件版本為 2.0.0.0 )
  • System.Web.WebPages 的組件版本編號為 2.1.0.0 ( GAC 組件版本為 2.0.0.0 )
  • System.Web.WebPages.Deployment 的組件版本編號為 2.1.0.0 ( GAC 組件版本為 2.0.0.0 )
  • System.Web.WebPages.Razor 的組件版本編號為 2.1.0.0 ( GAC 組件版本為 2.0.0.0 )

再者,我從  v2-rtm 分支下載回來的 5 個專案,其設定的組件編號為:

  • System.Web.Mvc 的組件版本編號為 4.0.0.0 ( 註冊在 GAC 的組件版本為 4.0.0.0 )
  • System.Web.Razor 的組件版本編號為 2.0.0.0 ( 註冊在 GAC 的組件版本為 2.0.0.0 )
  • System.Web.WebPages 的組件版本編號為 2.0.0.0 ( GAC 組件版本為 2.0.0.0 )
  • System.Web.WebPages.Deployment 的組件版本編號為 2.0.0.0 ( GAC 組件版本為 2.0.0.0 )
  • System.Web.WebPages.Razor 的組件版本編號為 2.0.0.0 ( GAC 組件版本為 2.0.0.0 )

各位看官看出端倪了嗎?v2-rtm 分支的原始碼,編譯出來的版本號將會與註冊在 GAC 的版本一致!

由於 .NET Framework 載入組件的順序,一定會先載入 GAC 的組件,然後才會嘗試載入 bin 目錄下的組件,不過,當 .NET Runtime 發現在 bin 目錄下的組件編號與 GAC 組件編號相同時,一律只會載入 GAC 組件!!!

這時有趣了!我還第一次遇到這種鬼打牆的事,我明明可以在 Visual Studio 2012 中順利編譯,按下 F12 執行「移至定義」( Go To Definition ) 也都運作正常,但網站執行起來後,一律只會載入 GAC 組件,不會載入 bin 目錄下的組件。不過,當我試圖刪除 bin 目錄下的 System.Web.Mvc.dll 組件後,竟然發生找不到組件的情況。我是不斷思索其原因,還問到在微軟美國總部擔任研發的朋友,就是百思不得其解,直到我終於看出是「組件編號」的差異後,這才真相大白。解決方法有二:

  • 透過 gacutil 工具,將 System.Web.Mvc.dll 與其他組件從 GAC 中移除 ( 感覺這不是個好方法 )
  • 只要修改掉專案中使用的組件編號,或直接使用 master 分支下的原始碼,也可以解決此問題。
    ( 因為 master 分支下的原始碼組件編號已經故意調整成 4.1.0.02.1.0.0,以前都不知道為何會這樣,到今天才恍然大悟!耗費精華 6 小時換來的~ T_T )

 

Step09:此時,體驗與 ASP.NET MVC 4.0 原始碼一起偵錯的過程,在預設的 HomeController 的 Index 動作方法加上一個中斷點,並按下【F5】功能鍵開始偵錯。

這時我們開啟「呼叫堆疊」窗格 ( Call Stack ) 就可以完整查詢 ASP.NET MVC 4.0 的原始碼了!

補充說明:由於 ASP.NET MVC 原始碼採用M icrosoft Public License (MS-PL) 開放原始碼授權,你甚至可以修改並重新散布修改過的 ASP.NET MVC 原始碼。筆者的公司也曾經因為 ASP.NET MVC 4.0 有部分功能無法符合專案需求而自行調整過,不過,如果你也想自行修改 ASP.NET MVC 的原始碼,必須要注意版本控管的問題。

 

最後,如果你還是想下載 v2-rtm 分支的原始碼,又要能正常編譯與執行的的話,如本文稍早所說,只要修改掉專案中使用的組件編號,就可以順利編譯,且正常的執行。

首先我開啟另一個專案,下載 v2-rtm 分支的原始碼,並加入專案、移除現有參考、重新加入專案參考後,開啟 System.Web.Mvc 專案的 Properties\CommonAssemblyInfo.cs 檔案。如下圖示:

這個 Properties\CommonAssemblyInfo.cs 檔案,是透過「連結檔案」的方式加入到各專案之中,所以基本上這個檔案會共用於所有 ASP.NET MVC 4 原始碼專案裡,你只要改一次,就可以了。

你可以看到這個檔案裡定義了各專案的 組件版本編號 (AssemblyVersion) 與 檔案版本編號 (AssemblyFileVersion) 如下:

只要把組件編號改掉,所有靈異現象就會全數消失:

 

好久沒這樣斬妖除魔了,蠻過癮的! (^_^)

也感謝讀者的力量,你們真的很認真地看完我書中每一個片段,這是對作者最大的鼓勵喔!

希望大家都能有所收穫!

 

相關連結