用 MSBuild 事件解決 ASP.NET Core 發佈 IIS 目錄之 DLL 鎖定問題
| | 5 | | ![]() |
開發測試 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 > "$(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) 功能。
【延伸閱讀】
- Visual Studio publish profiles (.pubxml) for ASP.NET Core app deployment
- Common macros for MSBuild commands and properties
- MSBuild Exec Task
- MSBuild CreateProperty task
- MSBuild Exec 輸出轉存屬性
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, 這也是選項,需動用管理者權限是小缺點。