這是改寫 ASP.NET Core 後的一大困擾,ASP.NET Core 預設會將 .cshtml 編譯成 YourApp.Views.dll,不再像以前可以直接修改 Views 資料夾下的 .cshtml,重新整理瀏覽器就看修改後的結果。必須停止偵錯,重新編譯執行才會更新。

這項調整讓我的前端程式開發過程變得很痛苦,我採用「輕前端」的開發策略,cshtml 是主體,JavaScript 部分只用 Vue.js 處理 MVVM 及用 AJAX 方式呼叫 WebAPI,因此常要反覆調整 HTML 元素的 v-model、v-bind、v-on 標籤及 CSS 樣式等,每動一個地方想看結果就得停止偵錯、重新編譯,再重新偵錯,看瀏覽器關了又開、開了又開,火都上來了。過去 MVC5 那種改 cshtml 後存檔 + 瀏覽器重整看結果的開發程序才是正常人能忍受的開發方式吧!

研究找到解決方案。在 MS 文件查到,ASP.NET Core 3.1 起支援所謂的 Razor Runtime Compilation,允許 Razor Pages 或 MVC 啟用 cshtml 動態編譯,恢復 MVC5 時代存檔重整即更新的模式。而從 VS2019 16.5.4 起(依文件釋出日期 4/14 推測),新增 ASP.NET Core 專案時會多一個「Enable Razor runtime compilation」選項:(實測要 ASP.NET Core 3.1 以後才會有這個選項)

勾選後,專案會多參照一個 Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation Package:

而 Properties/launchSettings.json 則有一個 ASPNETCORE_HOSTINGSTARTUPASSEMBLIES 變數啟用 cshtml 執行期編譯:

  //... 略
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
      }
    }
    //... 略

如果是已經建立好的 ASP.NET Core 3.1 專案,可以手動安裝 Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation NuGet Package,比照上述方式加入 ASPNETCORE_HOSTINGSTARTUPASSEMBLIES 參數。MS 文件提到 Startup.ConfigureServices() 加上 services.AddRazorPages().AddRazorRuntimeCompilation() 的做法,不如 用 ASPNETCORE_HOSTINGSTARTUPASSEMBLIES 變數動態切換來方便。

註:Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation 需要 .NET Core 3.1 以上版本,不適用 ASP.NET Core 2.x。

一般來說,cshtml 動態編譯在開發階段比較有用,在線上環境會因為動態編譯及啟用 .NET Core File Watcher 損失一些效能,而部署 .cshtml 檔案還會增加原始碼外洩及被惡意竄改的風險,較不建議使用。故預設 Publish 時,.cshtml 預設還是會編譯成 AppName.Views.dll,輸出結果也不會包含 Views 或 Pages 資料夾,若你有很好的理由要在正式環境啟用,可在 .csproj 加入 RazorCompileOnPublish 屬性設成 false (預設為 true):

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
    <RazorCompileOnPublish>false</RazorCompileOnPublish>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.3" />
  </ItemGroup>

</Project>

如此,輸出時將不會產生 AppName.Views.dll,MVC5 時代熟悉的 Views/Pages 資料夾也會回來囉。


Comments

# by Terence

謝謝大大的資訊, 小弟最近開始使用asp.net core開發, 覺得最困擾就是這件事...

# by wail

一直都覺得cshtml很麻煩 寫這東西都是一次定生死 以前的除錯都痛苦要命 現在改成要一直PLAY 真的讓人更想哭

# by Ike

推 dotnet watch + 啟動但不偵錯

# by oscar

請問大大,文章提到您用vuejs當前端的情況下,為什麼還要使用 cshtml 呢?主要目的是什麼呢?

# by Jeffrey

to oscar, 我偏好以 cshtml 為主體,Vue.js 只用來處理 MVVM,非 SPA 所以不需要 Vue-Router,沒用到 .vue 因此不用扯上 webpack 等編譯框架,我稱之「輕前端」,至於理由,這篇有較多說明: https://blog.darkthread.net/blog/html-inc-vuejs-lib/

# by Jin

我更討厭mvc強迫要把整個網站編譯成一個dll.. 每次要動code就要一直開分支, 不然就得保佑不要改到一半又有其他調整需求

# by Joe

請問黑大有沒遇過,RazorCompileOnPublish設定成false之後,publish部屬到iis上出現找不到Index.cshtml的情況,明明Views整個資料夾也有產生出來但是他找不到,我也在Program.cs加入了 UseContentRoot(Directory.GetCurrentDirectory()) 以下是錯誤訊息 System.InvalidOperationException: The view 'Index' was not found. The following locations were searched: /Views/Home/Index.cshtml /Views/Shared/Index.cshtml

# by Jeffrey

to Joe, 沒遇到過。可以開個新專案重現這個問題嗎?若可以,看要不要丟上 Github 分享讓大家抓蟲。

# by Joe

To 黑大, 把 iis 上部屬的資料夾移除再重新發布一次就正常了, 還是沒有抓到原本的蟲, 雖然解決了哈哈

# by jacky

請問這法有人在Blazor net5.0 適用嗎?試了沒產生Page .

# by Ho.Chun

寫得很詳細! 感謝 💪

Post a comment