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

今天有讀者反映我的書【[**ASP.NET MVC 4 開發實戰**](https://web.archive.org/web/20191112080721/https://www.delightpress.com.tw/book.aspx?book_id=sknp00028)】講到 **如何對 ASP.NET MVC 原始碼進行偵錯** 這一個技巧時，照著書操作沒有成功，我自己重新試了一次，還真的沒辦法，這才發現事情沒有想像中單純，魔鬼總在細節裡，我今天又花了 6 個小時反覆研究、測試、分析，這才終於釐清為什麼以前可以，現在卻無法對 ASP.NET MVC 4 原始碼進行偵錯的原因。因此我打算轉載書中的內容，並加以更新、補充，好讓大家能夠完整且順利的將 ASP.NET MVC 4 原始碼加入到專案之中。  
  
**※本篇文章****摘錄自筆者出版的【****[ASP.NET MVC 4 開發實戰](https://web.archive.org/web/20191112080721/https://www.delightpress.com.tw/book.aspx?book_id=sknp00028)****】一書，並且更新部分內容!  
**

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

> 注意：ASP․NET MVC 的原始碼專案已經移到 GitHub 存放，請[點擊這裡](https://github.com/aspnet/AspNetWebStack/tree/master/src/System.Web.Mvc)查看。

如果需要針對 ASP.NET MVC 4 原始碼一併偵錯，建議可以試試從 [CodePlex](https://github.com/aspnet/AspNetWebStack) 下載原始碼，與現有的 ASP.NET MVC 4 專案放在一起，這樣網站在執行時，就能同時查看 ASP.NET MVC 4 的原始碼與執行單步偵錯。

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

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

**Step01**：前往 [https://github.com/aspnet/AspNetWebStack](https://github.com/aspnet/AspNetWebStack) 並點擊 **SOURCE CODE** 頁籤進入原始碼頁面。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/b8cc444cfed48e1e4b3a-image_1898.png)

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

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/a5f29a9ab294153b3ff9-image_1899.png)

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

[![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/34845cc0eca8ae6980e1-image_thumb_149.png "image")](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/d5f5a4dd34abbeabe35d-image_1900.png)

**補充說明**：預設你在 **SOURCE CODE** 頁面所看到的 ASP.NET MVC 原始碼是 **master** 分支版本，代表的是持續**開發中的原始碼**；但是另外還有兩個分支分別是 **v2-rc** 與 **v2-rtm** 分支，分別代表著 **ASP.NET MVC 4 RC** 與 **ASP.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 1](https://visualstudio.microsoft.com/vs/older-downloads/) 與 [2012.2](http://www.asp.net/vnext/overview/fall-2012-update/aspnet-and-web-tools-20122-release-notes-rtw) 更新 (源碼有因為 [2012.2](http://www.asp.net/vnext/overview/fall-2012-update/aspnet-and-web-tools-20122-release-notes-rtw) 的出現做了些更新)

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

-   **.NET Framework 版本不相容!**

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

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/f9020bec006e83146708-image_1901.png)

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/3a9a7cfc0d92bcaaadfa-image_1902.png)

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

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

這時你搜尋到的原始碼版本編號為 [1b78397f32fc](https://github.com/aspnet/AspNetWebStack/commit/1b78397f32fc) ( 你也可以直接點選[這個連結](https://github.com/aspnet/AspNetWebStack/commit/1b78397f32fc)進入下載頁面 )

[![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/48f5b73a7248e3388b0d-image_thumb_150.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/7df743a7beac4d752443-image_1903.png)

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

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/58e59cb32786c5352f63-image_1904.png)

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/c9df105d91767f773232-image_1905.png)

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/16786abaaabfeafce84b-image_1906.png)

完成後如下圖示：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/a55abbb65a61f88fd5ce-image_1907.png)

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

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/ed545bc2ed6ce00e589b-image_1908.png)

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

-   **System.Web.Mvc**
-   **System.Web.Razor**
-   **System.Web.WebPages**
-   **System.Web.WebPages.Deployment**
-   **System.Web.WebPages.Razor**

完成後的圖示如下：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/1b02053f8aceb68ae19e-image_1909.png)

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/d0ccfafa977acb1db3f2-image_1910.png)

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/59b5ff6db842143b8916-image_1911.png)

**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'). ](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/69eddb3d64b343e6ba36-image_thumb_151.png)](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/0c6dcfd3fdf034c91fcc-image_1912.png)

文字錯誤訊息為：

\[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](http://msdn.microsoft.com/zh-tw/library/ex0ss12c\(v=vs.100\).aspx) 工具，將 System.Web.Mvc.dll 與其他組件從 GAC 中移除 ( 感覺這不是個好方法 )
-   只要修改掉專案中使用的組件編號，或直接使用 **master** 分支下的原始碼，也可以解決此問題。  
    ( 因為 **master** 分支下的原始碼組件編號已經故意調整成 **4.1.0.0** 與 **2.1.0.0**，以前都不知道為何會這樣，到今天才恍然大悟！耗費精華 6 小時換來的~ T\_T )

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/5795136e3b8ff51ce5a3-image_1913.png)

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/b35fdf38410312545547-image_1914.png)

**補充說明**：由於 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** 檔案。如下圖示：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/e7e1506ca1a6c6005d45-image_1915.png)

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

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/b4a93270c7d4f7a66a97-image_1916.png)

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

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/440bdd02da25277690c0-image_1917.png)

好久沒這樣斬妖除魔了，蠻過癮的！ (^\_^)

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

希望大家都能有所收穫！

**相關連結**

-   [ASP.NET MVC / Web API / Web Pages – Home](https://github.com/aspnet/AspNetWebStack) ( ASP.NET Web Stack Source )
-   [Getting Started With ASP.NET Web Stack Source on CodePlex](https://learn.microsoft.com/en-us/archive/blogs/henrikn/getting-started-with-asp-net-web-stack-source-on-codeplex)
-   [How to debug asp.net mvc 4 source code with Intellisense enabled?](http://forums.asp.net/t/1857937.aspx/1)
-   [look ma! no gac! razor intellisense without gacing](http://vibrantcode.com/2012/06/06/look-ma-no-gac/)
-   [ASP.NET Web Stack > Documentation >  NuGet Packages](https://github.com/aspnet/AspNetWebStack) ( 建置 **v2-rtm** 原始碼必看這篇 )
-   [How the Runtime Locates Assemblies](http://msdn.microsoft.com/en-us/library/yx7xezcf.aspx)
-   [Assembly Binding Log Viewer (Fuslogvw.exe)](http://msdn.microsoft.com/en-us/library/e74a18c4\(VS.71\).aspx)
-   [ASP.NET and Web Tools 2012.2 Release Notes](http://www.asp.net/vnext/overview/fall-2012-update/aspnet-and-web-tools-20122-release-notes-rtw)
-   [Gacutil.exe (全域組件快取工具)](http://msdn.microsoft.com/zh-tw/library/ex0ss12c\(v=vs.100\).aspx)
