热更新部署
1. 背景
当 ASP.NET Core 应用在 Windows 上运行时,二进制文件被锁定,因此无法对其进行修改或替换。每次发布API都需要停止站点或者关闭程序池。不是很方便,好在.net提供了解决方案: ShadowCopy
允许在应用运行时通过复制程序集来更新应用程序集。解决.netCore IIS站点发布时提示文件占用
1. 什么是 Shadow Copy?
Shadow Copy 是一种技术,用于创建文件或应用程序的副本,而不影响其原始版本。在 .NET 中,它允许应用程序在运行时加载程序集的副本,从而避免锁定原始文件。这在开发和部署场景中非常有用,因为它允许对程序集进行更新而不影响正在运行的应用程序实例。
2. Shadow Copy 的应用场景
- 热更新: 允许在不重启应用程序的情况下更新程序集。
- 单元测试: 在测试环境中加载程序集副本,避免测试代码影响生产代码。
- 插件系统: 在插件系统中加载独立的插件程序集,防止插件更新或卸载时影响主应用程序。
3. .net core 7中启用 ShadowCopy
ShadowCopy是通过自定义 web.config
中的 ANCM 处理程序设置来启用的:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<remove name="aspNetCore"/>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".logsstdout">
<handlerSettings>
<handlerSetting name="enableShadowCopy" value="true" />
<!-- Ensure that the IIS ApplicationPool identity has permission to this directory -->
<handlerSetting name="shadowCopyDirectory" value="../ShadowCopyDirectory/" />
</handlerSettings>
</aspNetCore>
</system.webServer>
</configuration>
processPath
arguments
这两个参数要替换掉, 否则站点启动不了:
processPath="dotnet" arguments="JOCAPI.Start.dll"
arguments
这个参数要替换成自己的项目启动项。
JOCAPI的web.config配置信息如下,直接复制即可:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath=".\JOCAPI.Start.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" >
<handlerSettings>
<handlerSetting name="enableShadowCopy" value="true" />
<!-- Ensure that the IIS ApplicationPool identity has permission to this directory -->
<handlerSetting name="shadowCopyDirectory" value="../ShadowCopyDirectory/" />
</handlerSettings>
</aspNetCore>
</system.webServer>
</location>
</configuration>
<!--ProjectGuid: 55982b09-7bd9-4c40-bb4d-b7257229f14f-->
4. 效果
配置完成后,当需要发布API后,无需再手动停止站点或程序池,直接替换dll文件即可,不会再提示文件占用了
目录中会自动生成一个文件夹:ShadowCopyDirectory
自动生成站点更新备份
其他参考
https://www.jianshu.com/p/ba86836caeeb
EF报错
如果把 {app}.deps.json
文件删掉之后, 执行带有 EF 查询的方法, 报了这么一个异常:
System.PlatformNotSupportedException: Strings.PlatformNotSupported_DataSqlClient
at Microsoft.Data.SqlClient.SqlConnectionStringBuilder..ctor(String connectionString)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerConnection.<>c.<get_IsMultipleActiveResultSetsEnabled>b__7_0(String cs)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerConnection.get_IsMultipleActiveResultSetsEnabled()
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerCompiledQueryCacheKeyGenerator.GenerateCacheKey(Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
为解决这个问题, 把发布选项的目标运行时 从 可移植
改为 win-x64
, 然后发布, EF 执行正常了..
日志文件
日志文件也被复制到了 ShadowCopy 文件夹中去了...
要解决这个问题,只能把所有日志目录指定站点文件外了。
不然真成扫地机器人扫狗屎,糊一地了。。。