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

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

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 還是會卡關。

於是,我再改良了做法,把放 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 > &quot;$(AppOfflinePath)&quot;"></Exec>
    <!--<Exec Command="ping -n 3 localhost > nul"></Exec>-->
  </Target>
  <Target Name="CustomActionsAfterPublish" BeforeTargets="AfterPublish">
    <Exec Command="del &quot;$(AppOfflinePath)&quot;"></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) 功能

【延伸閱讀】

Example of using batch and MSBuild event to create and delete app_offline.htm to solve ASP.NET Core dll lock issue in IIS.


Comments

# by Chin

请问一下前辈,我是用vscode开发ASP.Net Core,有没有办法达成以上的目的?

# by Jeffrey

to Chin, 文章提的兩種做法都適用 VSCode,按 Ctrl-Shift-` 開 Terminal 視窗,下指令 .\Publish.ps1 (批次檔) 或是 dotnet publish -c Release (MSBuild Task)。

# by Chin

to Jeffrey, 谢谢您的回复,我试看看

# by Gou

我是用powershell直接停止/啟動IIS內app pool Invoke-Command -Credential $Credential -ComputerName "Computer Name" -ScriptBlock { Stop-WebAppPool -Name "AppName" } dotnet publish Invoke-Command -Credential $Credential -ComputerName "Computer Name" -ScriptBlock { Start-WebAppPool -Name "AppName" }

# by Jeffrey

to Gou, 這也是選項,需動用管理者權限是小缺點。

Post a comment


45 + 16 =