Asp.net

ASP.NET MVC 5 發布預編譯問題

  • August 4, 2019

我過去曾使用 Web 發布工具發布我的 MVC 5 應用程序,而沒有在過去進行預編譯。為了減少每個頁面的初始載入時間,我修改了發佈設置,如下所示,以便在發布過程中預編譯應用程序。突然之間,我習慣的難以置信的可靠出版變成了一場噩夢。

  • 我對“將所有輸出合併到單個程序集”的理解意味著我的所有.cshtml頁面將一起編譯成Dashboard.Precompiled.dll,然後部署到 IIS。情況並非如此 - 當我能夠發布工作時,它會為我的項目.complied中的每個.cshtml文件創建一個文件並且不執行任何合併。
  • 現在的主要問題.compiled是 僅在某些時候生成。當我查看obj\Debug\AspnetCompileMerge\TempBuildDir\bin目錄時,沒有.compiled文件,也沒有Dashboard.Precompiled.dll.

我嘗試過重新啟動 Visual Studio,清理解決方案並重建,在發布前預覽與不預覽更改,創建全新的發布配置文件,並一次又一次地擺弄高級預編譯設置。通常在使用它大約 30 分鐘後,我可以.precompiled成功生成和發布文件,但是我無法確定是什麼原因導致它當時正常工作。下次我去發布而不更改任何設置時,它將再次停止工作。使用 VS2015 或 VS2017RC 時會出現此問題。

任何人都可以在這裡幫助我指出正確的方向嗎?我已經在這方面投入了很多小時,現在我覺得我在繞圈子。

謝謝!

編輯 我仔細查看了建構輸出,發現對 aspnet_compiler.exe 的呼叫是使用以下參數執行的:

C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_compiler.exe -v / -p C:\Users\steve\Source\Dashboard\master\src\Agility.Web\obj\Staging\AspnetCompileMerge\Source -d C:\Users\steve\Source\Dashboard\master\src\Agility.Web\obj\Staging\AspnetCompileMerge\TempBuildDir

當我直接從命令行執行此命令時,不會.compiledTempBuildDir\bin.

在此處輸入圖像描述

我想確切地找出兩者之間的區別:

  • “不要合併”和“不要合併。為每個頁面和控制項創建一個單獨的程序集。”

    • 聽起來這些都做同樣的事情,有什麼區別?
  • “將所有輸出合併到一個程序集”和“將所有頁面和控制輸出合併到一個程序集”

    • 同樣,這些聽起來像是同一件事(當您沒有App_Code文件夾時)。

而且我想找出為什麼“允許預編譯的站點可更新”在檢查時似乎沒有做任何事情(即我認為它會將頁面預編譯為他們自己的程序集/程序集,同時還發出原始的可編輯文件.*.aspx``.ascx``.master

因此,今天我坐下來創建了一個電子表格,並使用 ASP.NET WebForms *.csproj 應用程序執行了每個不同的發布配置文件預編譯設置 - 我還想看看最慢與最快的輸出是什麼。

背景:

  • 我的 ASP.NET 項目面向 .NET Framework 4.7.2
  • 它不是一個“網站項目”。
  • 原始 C#*.cs文件不會發佈到生產網路伺服器。
  • 它是一個使用*.aspx*.ascx*.master*.ashx的WebForms 項目Global.asax。它不是使用 Razor*.cshtml或 WebForms *.aspx視圖引擎的 MVC 項目)。
  • 它不使用App_Code文件夾(因此“視為庫組件”選項無效)。

發現:

重要說明

  • 這些發現僅適用於使用“傳統”ASP.NET Web 窗體 Web 應用程序項目的情況。這些不適用於 ASP.NET 網站項目(沒有*.csproj文件)、ASP.NET MVC(非核心)項目或 ASP.NET Core 項目。
  • 我使用術語“頁面文件”作為*.aspx*.ascx*.master*.asmx*.ashxfiles 的簡寫,但不是 Global.asax.
  • 我使用術語“正常建構”來指代在 Visual Studio 中執行“建構 > 重建項目”,您可以在其中看到%projectdir%\bin目錄中的輸出。這與執行發布建構(將首先執行正常建構,然後將輸出複製到另一個目錄以執行發布 MSBuild 步驟)進行比較

以下是我對每個選項的結果的發現:

  • “發布期間預編譯”(在發佈設置視窗中)

    • 如果您沒有App_Code文件夾並且想要發布可編輯的*.aspx/ *.ascx/`*.master 文件,則沒有性能理由來選中此框

      • 這是因為當選中此項但未選中“允許預編譯的站點可更新”,它只會預編譯您的Global.asax文件(而不是您的Global.asax.cs,無論如何都已編譯)。
      • 即您的*.aspx,*.ascx*.master文件*.ashx不會預編譯為程序集,它們仍然需要在 Web 伺服器上按需編譯。
      • 但它仍會預編譯它們以檢查<% @您的*.aspx*.ascx、和文件*.master中的編譯器錯誤和斷行。*.asax``*.ashx
  • “允許預編譯站點可更新”

    • 當這個檢查你的*.aspx,*.ascx*.master文件*.ashx不會預編譯成程序集,它們仍然需要在 web 伺服器上按需編譯。

      • 我最初認為它將這些文件預編譯為程序集(DLL)並另外發布原始*.aspx文件以在伺服器上進行編輯,並且只有在它們被更改時才重新編譯它們 - 但我錯了。
  • 發出調試資訊

    • *.pdb將為預編譯過程生成的每個新程序集生成文件。當您的應用程序正常建構時,它不會影響任何*.pdb已經存在的文件。
    • 我認為這應該始終啟用 - PDB 文件對於快速調查執行時問題至關重要,它們不會對最終發布大小增加太多。
  • 不要合併

    • 如果您沒有App_Code文件夾並選中“允許預編譯的站點可更新”,則“不合併”只會完全預編譯Global.asaxApp_global.asax.dll. 不會將其他 DLL 文件添加到最終發布輸出中。

    • 當“允許預編譯的站點可更新”未選中時,所有頁面文件(在上面的“重要說明”下定義)將被編譯成App_Web_xxxxxxxx.dll10 個類為一組的新 DLL 文件。

      • 我看不出它如何決定將文件分組為它使用的 10 個文件的模式——有時它們是按字母順序排列的,有時是任意的。
  • 不要合併。為每個頁面和控制項創建一個單獨的程序集。

    • 這與上面相同,除了每個程序集不是以 10 個頁面文件(或類)為一組,而是每個程序集 1個頁面文件
    • 當未選中“允許預編譯的站點可更新”時,這也是最慢的發布版本之一。
    • 這種方法的唯一優點是如果您想單獨替換*.dll伺服器上預編譯的每個頁面 - 但我認為這不是一個好主意,因為它經常會中斷 - 最好一次替換所有文件。僅當您使用 56K 連接並且一次只能上傳少於 100KB 時才這樣做——這很愚蠢。
  • 將所有輸出合併到一個程序集中

    • 確實將所有頁面文件 Global.asax( App_global.asax.dll) 編譯/合併為單個 DLL 文件。
  • 視為庫組件

    • 此選項對我的項目的影響為零,無論是選中還是未選中(因為我的項目沒有App_Code文件夾)。
  • 將每個單獨的文件夾輸出合併到自己的程序集中

    • 這會為項目中包含頁面文件的每個文件系統目錄生成中間 DLL ,然後將它們合併到每個文件夾的單個 DLL 中。
    • 此選項導致發布建構時間第二長。
    • 我想不出您今天需要使用此功能的充分理由 - 除非您有一個項目,其中有數千個頁面文件分佈在數十個文件夾中並且想要進行手動增量更新。(即這不是您在 CI/CD 過程中使用的選項)。
  • 將所有頁面和控制輸出合併到一個程序集中

    • 如果你有一個App_Code文件夾:

      • 然後App_Code(和其他“特殊文件夾”,如App_GlobalResources, App_WebReferences)的內容將從您的頁面文件程序集預編譯到這個單獨的程序集。這將包括Global.asax(編譯為App_global.asax.dll)。
    • 如果您沒有App_Code文件夾,則此選項會產生與“將所有輸出合併到單個程序集”非常相似的輸出,但最終輸出將預編譯Global.asax為其自己的程序集 ( App_global.asax.dll)。

      • 此選項導致所有選項的發布建構時間最長 - 在我的情況下,實際收益為零。
      • 因此,如果您沒有App_Code文件夾,則沒有理由選擇此選項。

重述:

當選中“允許預編譯站點可更新”並選中“不合併”時:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

   * Compiled only for error-checking.
   * Not compiled to an assembly DLL in the `bin\` folder.

Global.asax

   * Compiled to `App_global.asax.dll`

App_Code

   * Compiled to `App_Code.dll`

當未選中“允許預編譯站點可更新”並選中“不合併”時:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

   * Compiled to `App_Web_abcdefghij.dll` in groups of 10-per-DLL

Global.asax

   * Compiled to `App_global.asax.dll`

App_Code

   * Compiled to `App_Code.dll`

當未選中“允許預編譯站點可更新”並且選中“將每個單獨的文件夾輸出合併到其自己的程序集”時:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

   * Each file compiled to its own `App_Web_OriginalFileName.abcdefghij.dll` file.

Global.asax

   * Compiled to `App_global.asax.dll`

App_Code

   * Compiled to `App_Code.dll`

當未選中“允許預編譯站點可更新”並且選中“將所有輸出合併到單個程序集(名為 ‘Everything’)”時:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

   * Compiled and merged into the single Everything.dll

Global.asax

   * Compiled and merged into the single Everything.dll

App_Code

   * Compiled and merged into the single Everything.dll

當未選中“允許預編譯站點可更新”並且選中“將每個單獨的文件夾輸出合併到其自己的程序集”時:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

   * Compiled into an assembly for each folder.

Global.asax

   * Compiled to `App_global.asax.dll` (separate from the assembly for the *.aspx files in the root directory)

App_Code

   * Compiled and merged into `App_Code.dll`

選中“將所有頁面和控制項輸出合併到單個程序集(名為 ‘PagesAndControls’)”時:

*.aspx
*.ascx
*.ashx
*.asmx
*.master

   * Compiled into PagesAndControls.dll

Global.asax

   * Compiled to `App_global.asax.dll` (separate from PagesAndControls.dll)

App_Code

   * Compiled and merged into `App_Code.dll`

結論:

如果您在部署後不需要編輯*.aspx/ *.ascx,/*.master文件,並且您沒有App_Code文件夾,請選擇以下設置以獲得最佳效果:

[ ] Allow precompiled site to be updatable
[X] Emit debug information
[X] Merge all outputs to a single assembly 
[ ] Treat as library component

方法:

  • 所有建構都使用 Release。

  • 使用了“文件夾”發布配置文件。

    • 目標是同一磁碟捲(PCI-Express Optane 驅動器)上的文件夾。
    • 每次執行後都會清除該文件夾。
  • 每次測試執行之間的唯一變化是更改

  • git在每次建構和發布之前確認源文件和項目文件的零更改。

  • 我執行了一個 shell 腳本,它在每次執行之間完全清除binobj目錄,因此 Web 應用程序項目在執行之間完全重建,而不僅僅是 Publish)。

  • 我使用了一個秒錶程序,它記錄了我點擊“發布”按鈕的確切時間,但是當我看到“發布”操作完成時,它被鍵盤按下手動停止。

結果:

(我的電子表格的螢幕截圖)

預編譯輸入選項和觀察輸出的 Excel 電子表格螢幕截圖

Visual Studio 使用ASP.NET 編譯工具ASP.NET 合併工具來編譯 ASP.NET 應用程序Aspnet_compiler.exeAspnet_merge.exe

在幕後 VS 使用這 2 個工具來編譯 Web 應用程序項目。或者,您可以從命令行呼叫這兩個工具。

您可以通過導航到此目錄來找到這兩個文件:(%WINDIR%\Microsoft.NET\Framework\v4.0.30319或您正在使用的任何框架版本)。您可以使用這些工具來編譯 ASP.NET 應用程序。要了解有關這 2 個工具的所有選項的更多資訊,請閱讀以下連結:Aspnet_compiler.exeAspnet_merge.exe

我對解決問題的建議:

  • 重新啟動 Visual Studio
  • 清潔重建您的解決方案
  • 您應該只輸入不帶 .dll 的程序集的名稱(in your example Dashboard.Precompiled.dll should be just Dashboard.Precompiled)
  • (你可以考慮)重啟你的機器

請在此連結上閱讀有關高級預編譯設置的更多資訊,我也在此處粘貼選項:

允許預編譯站點可更新- 此設置對應於 aspnet_compiler.exe 命令的 -u 選項。如果選擇此選項,頁面和使用者控制項(.aspx、.ascx 和 .master 文件)將按原樣複製到目標文件夾,並且可以作為文本文件進行更新,而無需重新編譯項目。否則,頁面和使用者控制項的 HTML 標記將被刪除並編譯到程序集輸出中。

發出調試資訊- 此設置對應於 aspnet_compiler.exe 命令的 -d 選項。

不合併- 此設置不執行 aspnet_merge.exe 並且不使用 aspnet_compiler.exe 命令的 -fixednames 選項。

不要合併。為每個頁面和控制項創建單獨的程序集- 此設置不執行 aspnet_merge.exe。相反,它使用 aspnet_compiler.exe 命令的 -fixednames 選項。如果您想對已部署的網站進行精細更新,此選項很有用。但是,使用 -fixednames 選項進行編譯會禁用編譯器的批量優化,並可能導致大型網站的編譯時間更長。

將所有輸出合併到單個程序集- 此設置等效於 aspnet_merge.exe 命令的 -o assemblyname 選項。

視為庫組件(刪除 App_Code.compiled 文件) - 此設置對應於 aspnet_merge.exe 命令的 -r 選項。選擇此選項可以將項目的 App_Code.dll 程序集添加到另一個網站的 Bin 文件夾中,而不會與其他網站中的 App_Code.dll 程序集發生衝突。這對於建構 .ascx 控制項庫很有用

將每個單獨的文件夾輸出合併到其自己的程序集- 此設置對應於 aspnet_merge.exe 命令的 -prefix prefixname 選項。此選項使您能夠在文件夾級別更新您的網站,而不是更新整個應用程序。您可以使用可選程序集前綴框來指定一個前綴,該前綴將添加到所有生成的程序集名稱之前。例如,如果您指定前綴 MyCompany,則名稱將變為 MyCompany.SubfolderName。

將所有頁面和控制輸出合併到單個程序集- 此設置對應於 aspnet_merge.exe 命令的 -w 程序集名稱選項。此選項使您能夠獨立於更新其他程式碼來更新 UI 元素。App_Code、App_WebReferences 等特殊文件夾分別編譯為單獨的程序集。在“程序集名稱”框中指定目標程序集名稱。

引用自:https://stackoverflow.com/questions/42091663