dotnet publish偶发DLL被占用失败,回收应用池仍无法解决
问题成因
- 传统ASP.NET默认启用*影子复制(Shadow Copy)*机制,运行时会将站点DLL复制到临时目录加载,不会锁定站点根目录下的原始文件,因此不会出现发布时文件占用问题;而ASP.NET Core默认关闭该机制,IIS的w3wp进程会直接加载站点根目录的DLL并持有独占文件锁。
- IIS应用池回收是异步操作:执行
appcmd recycle命令返回成功仅代表IIS已下发回收指令,旧的w3wp进程需要等待现有请求处理完毕、资源完全释放后才会退出,锁释放存在几秒到几十秒的延迟。若回收后立刻执行发布命令,旧进程尚未释放锁就会触发文件占用报错。 - IIS默认启用重叠回收功能:回收时会先启动新的w3wp进程承接新请求,等旧进程处理完所有请求后再销毁旧进程,若新进程启动后立刻加载了站点目录的DLL,同样会导致发布时文件被锁。
解决方案
以下方案按优先级从高到低排序:
方案1:启用ASP.NET Core影子复制(最省心)
ASP.NET Core 3.1及以上版本原生支持影子复制功能,只需修改站点根目录的web.config配置,在aspNetCore节点添加shadowCopyEnabled="true"属性即可:
<configuration> <system.webServer> <aspNetCore processPath="dotnet" arguments=".\NeatF2PWebsite.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" shadowCopyEnabled="true" /> </system.webServer> </configuration>
配置生效后,ASP.NET Core会自动将DLL复制到临时目录加载,不会锁定站点根目录的原始文件,发布时无需操作应用池即可正常覆盖文件。
方案2:调整发布脚本逻辑,先停止再启动应用池
相比回收操作,先停止应用池可以强制进程退出释放锁,调整后的脚本如下:
ECHO ON REM 停止目标应用池 %SYSTEMROOT%\System32\inetsrv\appcmd stop apppool /apppool.name:"NeatF2P" REM 等待5秒确保进程完全退出、锁释放 timeout /t 5 /nobreak REM 执行发布 dotnet publish --output C:\Websites\NeatF2P --force --no-build REM 启动应用池 %SYSTEMROOT%\System32\inetsrv\appcmd start apppool /apppool.name:"NeatF2P" pause
如果服务器性能较低、进程释放慢,可以适当延长timeout的等待时间。
方案3:使用Web Deploy发布
Visual Studio自带的Web Deploy发布工具原生支持IIS站点发布,会自动处理文件锁、应用池启停等逻辑,无需自行编写脚本,适合日常开发使用。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

