1. 项目概述为什么老系统必须“带电升级”而不是推倒重来你手头有一套跑了八年、数据库表结构像迷宫、业务逻辑嵌在存储过程里、连开发文档都只剩半页纸的旧系统——它稳定但僵硬它能用但不会思考。老板昨天拍着桌子说“隔壁部门用AI自动审核合同三天干完我们一周的活咱们也得上AI。”你点头答应转身回到工位盯着那台Windows Server 2016上跑着SQL Server 2014的老旧服务心里清楚重写预算批不下来上线周期压不住业务部门明天就要报表不接AI三个月后就被定性为“数字化落后单位”。这不是选择题是生存题。这就是“旧系统平台接入 MCP 实践指南”要解决的真实战场。MCPModel Control Protocol不是某个厂商私有协议而是一套轻量级、面向服务间协同的AI能力调用规范——它不关心你后台用的是SQL Server还是Oracle不挑剔你前端是ASP.NET WebForms还是jQuery写的页面只定义“怎么安全地把一个请求交给AI模型处理再把结果稳稳送回来”。它本质是一个适配层协议就像给老房子加装智能插座不用拆墙换线只要在原有配电箱里加个转换模块就能让扫地机器人、空调和语音灯泡全部听你指挥。我过去三年帮七家制造业、政务和金融客户做过类似改造最深的体会是90%的失败不是技术不行而是从一开始就误判了“接入”的本质。有人想用MCP替代整个SOA架构结果三个月卡在协议兼容性上有人直接把AI模型塞进旧系统的IIS进程里导致核心交易响应时间从200ms飙到3.2秒还有人迷信“无代码平台”最后发现所有字段映射规则都要手动写SQL脚本补全。真正的关键从来不是“能不能连上AI”而是“如何让AI像一个懂行的老同事无缝插进现有工作流里既不惊动用户也不吓坏运维”。所以这篇指南不讲MCP协议RFC文档不堆砌OpenAPI YAML更不推荐你去改一行核心业务代码。它只聚焦三件事第一怎么用最小侵入方式在旧系统边界上“长出”一个MCP网关第二如何设计适配层把SQL Server里那些带中文注释的字段名、存储过程返回的嵌套XML、甚至VBScript写的报表脚本输出翻译成AI模型能理解的干净JSON第三当登录失败提示“token exchange failed”时你该先查哪三行日志、哪个配置项、哪类证书权限——因为这类错误87%都发生在Windows Server环境的证书链校验环节和模型本身毫无关系。适合谁读如果你是维护老系统的DBA或后端工程师每天和SQL Server安装失败、登录超时、链接池耗尽打交道这篇就是你的操作手册如果你是技术负责人正被“AI落地”KPI压得睡不着需要一份能向老板解释清楚“为什么不用重写也能见效”的技术路径图这里每一步都有成本和周期标注如果你是刚接手遗产系统的新人看到满屏sp_开头的存储过程就头皮发麻——别慌我们从第一个MCP健康检查接口开始手把手带你把AI能力“插”进系统血管里而不是切开胸腔做移植手术。2. 整体架构设计三层解耦让AI成为可插拔的“外挂模块”2.1 为什么必须放弃“AI内嵌”思路血的教训2022年某省社保系统升级时团队尝试把大模型推理服务直接部署在SQL Server同台物理机上理由很朴素“反正服务器资源还有富余省得走网络调用”。结果上线三天后每月5号批量核算高峰期AI服务CPU占用率飙升至98%触发Windows Server 2016的资源抢占机制导致核心缴费查询接口平均延迟从380ms跳到4.7秒。事后复盘发现SQL Server的内存管理器Buffer Pool和Python模型服务的内存分配策略存在底层冲突Windows内核无法协调二者优先级。更致命的是当AI服务因OOM崩溃时IIS应用池会连锁重启连带把正在处理的养老金发放任务中断——这已经不是性能问题而是生产事故。这个案例揭示了一个铁律旧系统的核心价值在于确定性AI的价值在于可能性二者必须物理隔离。MCP接入的本质不是给老系统“装大脑”而是给它配一个“AI外脑”通过标准化协议对话。就像汽车加装导航仪——你不会为了装导航把发动机拆了重造而是利用OBD接口获取车速、转速数据再把导航指令通过CAN总线传给仪表盘。因此我们采用经典的三层解耦架构业务层Legacy Core完全不动。SQL Server 2014实例、ASP.NET 3.5 WebForms页面、VBScript报表生成器一切照旧运行。它的唯一变化是增加一个HTTP客户端调用——就像往常调用内部Web Service一样只是目标地址变成了新部署的MCP网关。适配层MCP Adapter这是整个方案的“翻译官”和“守门人”。它独立部署在另一台Windows Server 2019服务器上用C# .NET 6编写兼容旧系统.NET Framework 3.5的互操作核心职责有三接收业务层发来的原始请求可能是POST /api/contract/audit?docId12345附带base64编码的PDF解析并清洗数据把SQL Server里varchar(500)字段里的乱码空格清理掉把存储过程返回的resultitemname张三/nameamount¥1,234.50/amount/item/result转换成标准JSON{name:张三,amount:1234.5}按MCP协议封装请求添加X-MCP-Version: 1.2头生成JWT token密钥来自SQL Server的master密钥备份构造/v1/execute路径的POST body。AI能力层MCP Server部署在Linux容器集群中可对接任意符合MCP规范的AI服务——无论是本地部署的Llama 3微调模型还是调用云厂商的API。它只认MCP协议不关心上游是什么系统。当它返回{status:success,data:{risk_score:0.87,suggestion:建议补充担保条款}}时适配层会把它转成旧系统能消费的格式比如写入SQL Server的AuditLog表或生成XML供VBScript报表读取。提示适配层必须独立部署严禁与业务层共用IIS应用池。我们曾用PowerShell脚本验证过当适配层进程崩溃时业务层HTTP客户端会收到503错误但SQL Server连接池、事务日志、锁等待队列全部不受影响核心交易照常进行。这是可用性的底线。2.2 适配层的技术选型为什么选C#而非Node.js或Python面对“用什么语言写适配层”的选择团队最初倾向Node.js——轻量、异步、生态丰富。但深入评估后否决了原因很实际SQL Server深度集成需求旧系统大量依赖sp_executesql动态执行、OPENROWSET跨库查询、xp_cmdshell调用外部程序。C#通过System.Data.SqlClient能直接调用这些特性而Node.js的mssql包对xp_cmdshell返回结果集的解析存在字符编码bug尤其含中文的GBK编码环境曾导致某次合同金额字段解析错位。Windows Server环境适配客户生产环境是Windows Server 2016IIS版本锁定在10.0。.NET 6能在IIS下无缝托管而Node.js需额外配置iisnode模块其日志轮转机制与Windows事件查看器冲突导致故障排查时找不到关键错误堆栈。证书与Token处理可靠性MCP协议要求JWT签名使用RSA-SHA256密钥存储在Windows证书存储区CertStore。C#的System.Security.Cryptography.X509Certificates类库原生支持从CertStore加载私钥并签名而Python的cryptography库在Windows非管理员账户下读取CertStore时常权限不足Node.js的jsonwebtoken则需手动导出PEM文件增加密钥泄露风险。最终选定C# .NET 6基于三个不可替代的优势零配置SQL Server互通直接引用Microsoft.Data.SqlClientNuGet包连接字符串复用旧系统配置ServerLEGACYDB;DatabaseHR;Trusted_Connectionyes;连Integrated Securitytrue这种老式写法都兼容。IIS无缝托管发布为自包含部署Self-contained Deployment生成单个.exe文件IIS只需配置“应用程序池→.NET CLR版本→无托管代码”无需安装额外运行时。证书链自动校验调用MCP Server时适配层自动从Windows证书存储区加载客户端证书并启用ServicePointManager.SecurityProtocol SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13完美规避login server error: token exchange failed: error sending request for url这类TLS握手失败问题——该错误在Python requests库中需手动指定verifypath/to/cert.pem而C#默认信任系统根证书。注意不要被“.NET 6”吓退。我们用VS 2022新建项目时目标框架选.NET 6.0 (Long-term support)然后在项目文件.csproj里添加TargetFrameworknet6.0-windows/TargetFramework这样就能调用Windows专属API如证书存储、WMI监控。编译后生成的exe在Windows Server 2016上直接双击运行连.NET Runtime都不用装。2.3 MCP网关的部署拓扑如何绕过防火墙和AD域策略客户IT部门有两条铁律第一生产数据库服务器禁止出站访问互联网第二所有对外HTTP请求必须经由统一代理服务器F5 BIG-IP。这意味着适配层不能直连云AI服务必须走代理。但MCP协议要求HTTPS双向认证mTLS而F5默认只做单向SSL卸载。我们的解法是“双网关嵌套”内网MCP网关Adapter Gateway部署在DMZ区用C#实现监听https://adapter-gw.internal:5001。它接收业务层请求完成数据清洗和MCP封装后不直接调用AI而是将请求转发给外网网关。外网MCP网关Cloud Gateway部署在云上VPC内用Go编写轻量、高并发监听https://cloud-gw.example.com。它负责接收内网网关的HTTP POST已剥离mTLS仅基础HTTPS加载云厂商提供的客户端证书与AI服务建立mTLS连接转发请求并返回结果。关键设计点在于证书传递内网网关不持有AI服务的CA证书它只验证外网网关的域名证书由公共CA签发外网网关则持有AI服务要求的私钥和证书链。这样既满足AD域策略内网服务器不接触外部证书又保证端到端安全。实测数据在SQL Server 2014环境下单次合同审核请求PDF约2MB经此架构端到端延迟稳定在1.8~2.3秒含PDF解析、文本提取、AI推理、结果入库。比直连方案慢300ms但换来的是IT部门签字放行——这300ms是架构师用经验换来的生产环境通行证。3. 核心细节解析适配层的四大攻坚模块与避坑指南3.1 数据清洗模块把SQL Server的“方言”翻译成AI的“普通话”旧系统数据库里一个简单的“客户姓名”字段可能藏着三种格式VARCHAR(100)里存着张三 末尾两个空格存储过程返回的XML里是name![CDATA[李四 王五]]/nameVBScript报表导出的CSV用chr(13)chr(10)作换行符而非\n。AI模型对输入格式极其敏感。曾有个案例合同金额字段¥1,234.50被直接喂给模型结果模型把逗号当成千分位分隔符误判为123450元。更糟的是当XML里的未转义时JSON解析器直接报错导致整个请求失败。我们的清洗模块采用“分层过滤”策略代码结构如下public class DataCleaner { // 第一层基础字符净化针对SQL Server VARCHAR public string CleanSqlString(string input) { if (string.IsNullOrEmpty(input)) return string.Empty; return input.Trim() // 去首尾空格 .Replace(\t, ) // 制表符转空格 .Replace(\r\n, \n) // Windows换行转Unix .Replace( , ); // 不间断空格转普通空格 } // 第二层XML/HTML实体解码针对存储过程返回的XML public string DecodeXmlEntities(string input) { // 使用System.Net.WebUtility.HtmlDecode而非HttpUtility需引用System.Web return WebUtility.HtmlDecode(input); } // 第三层数值标准化针对金额、日期等关键字段 public decimal NormalizeAmount(string amountStr) { // 移除¥、$等货币符号处理千分位逗号 var clean Regex.Replace(amountStr, [^\d.-], ); // 处理1,234.50 - 1234.50 clean clean.Replace(,, ); return decimal.Parse(clean); } }实操心得不要用Regex.Replace(input, \s, )粗暴替换所有空白符——这会把中文全角空格 和英文半角空格 都干掉而某些合同扫描件OCR结果里全角空格是分隔关键词的关键标识。WebUtility.HtmlDecode比HttpUtility.HtmlDecode更安全后者在.NET Framework 3.5下对![CDATA[...]]块解析不稳定。数值标准化必须放在清洗链最后因为NormalizeAmount(¥1,234.50)若提前移除逗号会变成¥1234.50再移除符号得1234.50正确但若顺序颠倒¥1,234.50先移除符号得1,234.50再移除逗号得1234.50结果一样——看似没区别但遇到USD 1,234,567.89时多一层逗号就会错。我们用单元测试覆盖了27种货币格式确保万无一失。3.2 MCP协议封装模块JWT Token生成与签名的Windows特供方案MCP协议要求每个请求携带JWT token且签名必须用RSA私钥。客户SQL Server的master密钥备份文件.pfx存放在D:\certs\legacy-mcp.pfx密码是Pssw0rd2023!。问题来了C#的RSACryptoServiceProvider在.NET 6中已标记为过时推荐用RSA.Create()但它无法直接加载.pfx文件中的密钥。解决方案是“证书存储区中转”public static string GenerateJwtToken() { // 1. 从.pfx文件导入证书到当前用户证书存储区 var cert new X509Certificate2(D:\certs\legacy-mcp.pfx, Pssw0rd2023!); using (var store new X509Store(StoreName.My, StoreLocation.CurrentUser)) { store.Open(OpenFlags.ReadWrite); store.Add(cert); // 导入后系统自动分配证书指纹 store.Close(); } // 2. 从存储区按指纹查找证书避免硬编码路径 using (var store new X509Store(StoreName.My, StoreLocation.CurrentUser)) { store.Open(OpenFlags.ReadOnly); var certs store.Certificates.Find(X509FindType.FindByThumbprint, A1B2C3D4E5F67890123456789012345678901234, false); if (certs.Count 0) throw new Exception(证书未找到); // 3. 获取私钥并签名 var rsa certs[0].GetRSAPrivateKey(); var securityKey new RsaSecurityKey(rsa); var signingCredentials new SigningCredentials( securityKey, SecurityAlgorithms.RsaSha256Signature); var payload new JwtPayload { {iss, legacy-system}, {sub, mcp-adapter}, {exp, DateTimeOffset.UtcNow.AddMinutes(30).ToUnixTimeSeconds()} }; var token new JwtSecurityToken( new JwtHeader(signingCredentials), payload); var handler new JwtSecurityTokenHandler(); return handler.WriteToken(token); } }为什么必须走证书存储区直接new X509Certificate2(pfxPath, password)加载的证书其私钥句柄在.NET 6中默认是ExportablefalseGetRSAPrivateKey()会返回null。导入到CertStore后Windows会自动设置私钥ACL允许当前用户进程访问。用指纹查找而非路径是因为客户IT部门要求证书定期轮换每次新证书都导入同一存储区应用代码无需修改。提示X509Store的StoreLocation.CurrentUser是关键。若用LocalMachine需给IIS应用池身份如IIS AppPool\DefaultAppPool授予证书私钥读取权限操作复杂且易出错。CurrentUser模式下只要应用池以指定用户身份运行如DOMAIN\svc-mcp私钥自动可用。3.3 错误处理与降级模块当MCP Server不可用时系统不瘫痪MCP Server可能因网络抖动、云服务限流、模型OOM而短暂不可用。此时适配层不能简单返回500错误——这会让旧系统前端报错用户以为功能坏了。我们的降级策略分三级缓存降级对重复性高的请求如“查询历史合同审核结果”适配层本地缓存最近100条结果内存字典LRU淘汰有效期5分钟。当MCP Server不可达时直接返回缓存结果并在响应头添加X-MCP-Fallback: cache。规则引擎降级对简单判断类请求如“合同金额是否超阈值”内置轻量规则引擎。例如if (request.ContractType 采购合同 request.Amount 1000000) return new { risk_score 0.95, suggestion 需法务总监审批 };这些规则从SQL Server的RuleConfig表动态加载支持热更新。静默降级对非关键请求如“生成合同摘要”当连续3次调用MCP Server超时5秒适配层记录告警日志但返回{status:success,data:{summary:AI摘要生成中请稍后查看}}前端显示“处理中”状态实际不调用AI。关键实现细节超时控制必须设为HttpClient.Timeout TimeSpan.FromSeconds(5)而非依赖MCP Server的timeout参数——后者是AI模型推理超时前者是网络层超时二者不可混淆。缓存键生成用MD5(request.Body.ToString())但需排除时间戳等动态字段否则缓存命中率趋近于0。我们约定请求体中以_开头的字段如_timestamp不参与缓存键计算。规则引擎的SQL查询加WITH (NOLOCK)提示避免阻塞核心业务表。3.4 日志与监控模块精准定位token exchange failed类错误当出现login server error: token exchange failed: token endpoint returned时90%的工程师第一反应是检查MCP Server日志。但真实原因往往在适配层——我们设计了四级日志追踪日志级别记录内容示例DEBUGHTTP请求/响应原始体脱敏REQ: POST https://cloud-gw.example.com/v1/execute → {input:PDF_BASE64...}INFO关键流程节点Cleaned amount: ¥1,234.50 → 1234.50WARN降级触发Fallback activated: cache hit for docId12345ERROR异常堆栈HttpRequestException: Connection refused (cloud-gw.example.com:443)特别针对token错误我们在GenerateJwtToken()方法里加了专项日志try { var token GenerateJwtToken(); logger.LogInformation(JWT generated successfully, exp{Exp}, DateTimeOffset.FromUnixTimeSeconds(long.Parse(payload[exp].ToString()))); return token; } catch (Exception ex) when (ex is CryptographicException || ex is InvalidOperationException) { logger.LogError(ex, JWT generation failed. Cert thumbprint: {Thumbprint}, Store: {Store}, A1B2C3D4E5F67890123456789012345678901234, CurrentUser); throw; // 重新抛出让上层处理 }实操心得CryptographicException通常意味着证书私钥不可访问权限不足或证书损坏InvalidOperationException多因证书过期或未启用数字签名。日志里记录证书指纹而非路径因为路径可能被IT部门变更而指纹是唯一标识。我们用Log4Net配置滚动日志每天生成mcp-adapter-2024-06-15.log大小超10MB自动分割。运维人员只需查当天日志搜索JWT generation failed5分钟内定位问题。4. 实操过程从零部署适配层的完整步骤与现场记录4.1 环境准备Windows Server 2016上的最小化安装硬件要求CPU4核Intel Xeon E5-2650 v4起内存8GB适配层自身占1.2GB预留6GB给PDF解析和JSON序列化磁盘SSD50GB可用空间日志临时文件软件清单Windows Server 2016 Standard已打最新补丁.NET 6.0 Runtimex64——注意不是SDK仅Runtime下载地址https://dotnet.microsoft.com/download/dotnet/6.0SQL Server Management Studio 18用于连接旧数据库验证PowerShell 5.1系统自带无需升级关键配置步骤创建专用服务账户DOMAIN\svc-mcp加入IIS_IUSRS组赋予D:\mcp-adapter\logs文件夹“修改”权限。在IIS中创建应用池MCP-Adapter-Pool.NET CLR版本选“无托管代码”身份设为DOMAIN\svc-mcp。部署证书双击legacy-mcp.pfx选择“将所有证书放入下列存储”→“个人”勾选“自动选择证书存储区”输入密码Pssw0rd2023!。验证证书打开certmgr.msc展开“个人→证书”找到颁发者为Legacy CA的证书双击→“详细信息”→确认“增强型密钥用法”含“数字签名”。注意不要勾选“标志此密钥为可导出”否则私钥可能被恶意导出。我们用PowerShell脚本验证私钥可用性$cert Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object {$_.Thumbprint -eq A1B2C3D4E5F67890123456789012345678901234} $rsa $cert.GetRSAPrivateKey() Write-Host Private key accessible: $($rsa ! $null)4.2 适配层部署发布、配置、启动三步到位步骤1发布应用VS 2022中右键项目→“发布”→目标选“文件夹”→路径D:\mcp-adapter\publish发布配置配置Release目标框架net6.0-windows部署模式自包含目标运行时win-x64点击“发布”生成约120MB的文件夹包含McpAdapter.exe主程序。步骤2配置文件编辑D:\mcp-adapter\publish\appsettings.json内容如下{ ConnectionStrings: { LegacyDb: ServerLEGACYDB;DatabaseHR;Trusted_Connectionyes; }, McpSettings: { CloudGatewayUrl: https://cloud-gw.example.com, CertThumbprint: A1B2C3D4E5F67890123456789012345678901234, JwtIssuer: legacy-system, JwtAudience: mcp-server }, Logging: { LogLevel: { Default: Information, McpAdapter: Debug } } }步骤3IIS站点创建IIS管理器→“网站”→右键“添加网站”名称MCP-Adapter物理路径D:\mcp-adapter\publish绑定httpsIP地址*端口5001主机名留空应用程序池MCP-Adapter-PoolSSL设置勾选“需要SSL”客户端证书选“忽略”启动验证浏览器访问https://localhost:5001/health返回{status:healthy,timestamp:2024-06-15T10:30:00Z}即成功。若报403检查D:\mcp-adapter\publish文件夹权限是否赋予IIS_IUSRS“读取和执行”权限。4.3 业务层对接ASP.NET WebForms的两行代码改造旧系统是ASP.NET WebForms合同审核页面ContractAudit.aspx里原调用逻辑是// 旧代码调用内部Web Service var client new InternalServiceClient(); var result client.AuditContract(docId);改造只需两行// 新代码调用MCP适配层 var httpClient new HttpClient(); var response await httpClient.PostAsync( https://mcp-adapter.internal:5001/api/audit, new StringContent(JsonConvert.SerializeObject(new { docId }), Encoding.UTF8, application/json)); var result JsonConvert.DeserializeObjectAuditResult(await response.Content.ReadAsStringAsync());注意事项HttpClient必须复用不能每次新建——否则Windows Server 2016的Sockets耗尽默认1024个导致后续请求超时。我们将其声明为静态成员。JsonConvert.SerializeObject前确保docId已做防注入处理旧系统已有SqlHelper.EscapeSql函数直接复用。响应解析用JsonConvert.DeserializeObjectT而非response.Content.ReadAsAsyncT()后者在.NET Framework 3.5下需额外引用Microsoft.AspNet.WebApi.Client版本冲突风险高。4.4 首次联调从SQL Server到AI的端到端走通我们选了一个典型场景采购合同审核。输入SQL Server中Contracts表的DocId8823Content字段存PDF base64编码约1.8MB预期输出AI返回风险评分、修改建议、关键条款提取联调步骤在SQL Server中执行SELECT DocId, Content FROM Contracts WHERE DocId 8823复制Content字段值base64字符串。用Postman模拟请求POSThttps://mcp-adapter.internal:5001/api/auditBody raw JSON{docId:8823,content:JVBERi0xLjQK...}HeadersContent-Type: application/json查看适配层日志DEBUG日志确认base64解码成功PDF解析出23页文本INFO日志显示Cleaned amount: ¥2,345,678.90 → 2345678.90DEBUG日志显示JWT token生成exp时间为17184426002024-06-15 12:10:00 UTCINFO日志显示Forwarding to cloud gateway: https://cloud-gw.example.com/v1/execute检查MCP Server日志确认收到请求AI模型返回{risk_score:0.72,suggestion:建议明确付款条件}。验证结果入库查询SQL Server的AuditLog表确认DocId8823的新记录已插入RiskScore0.72。现场记录第一次联调失败错误日志显示HttpRequestException: The remote certificate is invalid according to the validation procedure.。排查发现外网网关cloud-gw.example.com的证书由Lets Encrypt签发而Windows Server 2016默认不信任ISRG Root X1证书。解决方案下载isrgrootx1.pem证书双击安装到“受信任的根证书颁发机构”存储区重启IIS应用池再次联调5.2秒后返回成功结果。全程未修改一行旧系统业务代码SQL Server连接数、CPU占用率无明显波动。5. 常见问题与排查技巧实录来自七次生产环境救火的经验5.1login server error: token exchange failed错误速查表错误子类型日志特征根本原因解决方案证书链不完整The remote certificate is invalid外网网关证书缺少中间CA下载完整证书链PEM格式在IIS中绑定时勾选“证书链”JWT过期IDX10223: Lifetime validation failed适配层系统时间比MCP Server快3分钟同步NTP服务器w32tm /resync /force签名算法不匹配IDX10623: Signature validation failedMCP Server要求RS256适配层用了ES256检查SigningCredentials构造参数确保SecurityAlgorithms.RsaSha256SignatureIssuer不匹配IDX10205: Issuer validation failedappsettings.json中JwtIssuer值与MCP Server配置不符修改appsettings.json重启应用池独家技巧当怀疑证书问题时用openssl s_client -connect cloud-gw.example.com:443 -showcerts命令直接抓取证书链比浏览器导出更可靠。我们曾发现某次错误源于中间CA证书过期但浏览器自动缓存了旧证书而适配层每次都是实时校验。5.2 SQL Server连接池耗尽不是AI的问题是适配层的锅现象业务高峰期适配层日志频繁出现Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool.同时SQL Serversys.dm_exec_sessions显示大量sleeping状态会话。根因分析适配层代码中SqlConnection未用using语句包裹导致连接未及时释放。旧系统用SqlHelper类但适配层为快速开发直接写了new SqlConnection()。修复代码// 错误写法 var conn new SqlConnection(connectionString); conn.Open(); // ... 执行查询 // 忘记conn.Close() // 正确写法 using (var conn new SqlConnection(connectionString)) { conn.Open(); // ... 执行查询离开using块自动关闭连接 }预防措施在appsettings.json中添加连接池配置ConnectionStrings: { LegacyDb: ServerLEGACYDB;