3

用 MSBuild 事件解決 ASP.NET Core 發佈 IIS 目錄之 DLL 鎖定問題

 1 year ago
source link: https://blog.darkthread.net/blog/appoffline-msbuild-task/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

用 MSBuild 事件解決 ASP.NET Core 發佈 IIS 目錄之 DLL 鎖定問題-黑暗執行緒

開發測試 ASP.NET 專案我有個常用的小技巧 - 將 publish 輸出目的地設成 IIS 應用程式主目錄。如此不用啟動 Visual Studio 也能測試,每次修改程式後,使用指令 dotnet publish -c Release 或在 Visual Studio 執行 Publish 便能換版。

Fig1_637917394532626756.png

在實務應用時會遇到一個問題,若更新時有人剛瀏覽過網站,IIS 網站應用程式在啟動中,WebAppName.dll 將會被鎖定無法覆寫,導致發佈失敗!

Fig2_637917394534037996.png

ASP.NET 時代,我們可以隨時覆寫 bin 目錄的 DLL 觸發 IIS AppPool 重啟;在 ASP.NET Core 則必須先停止 AppPool 才能覆寫 DLL 檔。關於這個議題,更多細節可參考保哥這篇 - 如何正確地發行 ASP.NET Core 網站到遠端 IIS 站台,除了使用 PowerShell 先停止 AppPool 再複製檔案,更簡便的的做法是先在網站根目錄下放一個 app_offline.htm 檔案,IIS 在偵測到檔案出現便會讓網站應用程式離線,AppPool 停止後即可覆寫檔案,更新完成後再刪除 app_offline.htm 讓網站上線。

若要讓這一連串動作一氣喝成,可在專案根目錄放個 Publish.ps1,用它取代單純的 dotnet publish 指令:

$pubFolder = "$PSScriptRoot\bin\Release\net6.0\publish"
$appOfflineHtm = "$pubFolder\app_offline.htm";
New-Item $appOfflineHtm -ItemType "file"
dotnet publish -c Release
Remove-Item $appOfflineHtm

但這個做法,變成得手動執行,若是在 Visual Studio 按 Publish 還是會卡關。

Fig3_637917394534571256.png

於是,我再改良了做法,把放 app_offline.htm 跟刪掉 app_offline.htm 寫成 Publish 前後的事件 (CustomActionsBeforePublish 及 CustomActionsAfterPublish):

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

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <Target Name="CustomActionsBeforePublish" BeforeTargets="BeforePublish">
	<CreateProperty Value="$(ProjectDir)$(PublishDir)app_offline.htm">
      <Output TaskParameter="Value" PropertyName="AppOfflinePath"/>
	</CreateProperty>
    <Exec Command="echo UPDATING > "$(AppOfflinePath)""></Exec>
    <!--<Exec Command="ping -n 3 localhost > nul"></Exec>-->
  </Target>
  <Target Name="CustomActionsAfterPublish" BeforeTargets="AfterPublish">
    <Exec Command="del "$(AppOfflinePath)""></Exec>
  </Target>

</Project>

補充說明,實測 Visual Studio Publish 時 AfterPublish 的 $(PublishDir) 變數會變成 \obj\Release\net6.0\PubTmp\Out\,故我加了一個 CreateProperty Task 儲存 app_offline.htm 路徑;另外,IIS 偵測到 app_offline.htm 後會通知 ASP.NET Core 程式關機,但有時需等待一些善後動作完成才真的結束,在此之前檔案仍會被鎖定,Publish 本身具有重試機制,或者也可在放入 app_offline.htm 後加一個 ping -n 秒數 localhost > nul 等待一段時間再覆寫 DLL。

最後,如果是 ASP.NET Core 6 專案,目前有個實驗性質的陰影複製(Shadow-Copying)功能,能更優雅地解決執行中網站部署問題,詳情可參考保哥的文章 - 如何啟用 ASP.NET Core 6.0 部署到 IIS 的陰影複製 (Shadow-copying) 功能

【延伸閱讀】


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK