如何在ASP.NET Core中捕获无服务器SQL Server冷启动异常并返回友好提示
解决方案
完全可以实现,你只需要先识别Azure无服务器SQL冷启动对应的特定异常类型和错误码,再通过全局异常处理机制捕获即可。
第一步:识别冷启动对应的特定异常
Azure无服务器SQL冷启动触发的连接超时/暂时不可用异常属于Microsoft.Data.SqlClient.SqlException,你可以通过匹配错误编号精准区分这类异常:
- 连接超时错误号:
-2 - 数据库冷启动中暂时不可用错误号:
40613
第二步:捕获异常并返回友好提示
推荐使用全局异常中间件方案,可覆盖所有业务场景的异常捕获:
方案1:全局异常中间件(全场景覆盖)
- 编写自定义中间件代码:
using Microsoft.Data.SqlClient; using Microsoft.AspNetCore.Http; using System.Threading.Tasks; public class SqlColdStartHandleMiddleware { private readonly RequestDelegate _next; public SqlColdStartHandleMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (SqlException ex) when (ex.Number is -2 or 40613) { // 匹配到冷启动异常,返回自定义提示 context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; // 如果是接口请求返回JSON if (context.Request.Path.StartsWithSegments("/api")) { context.Response.ContentType = "application/json"; await context.Response.WriteAsJsonAsync(new { msg = "数据库正在预热,请10秒后刷新重试", retryAfter = 10 }); } // 如果是页面请求跳转至友好提示页 else { context.Response.Redirect("/DbWarmingTip"); } } } }
- 在
Startup.cs的Configure方法最顶部注册中间件,确保优先级高于其他业务中间件:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseMiddleware<SqlColdStartHandleMiddleware>(); // 其余中间件(路由、鉴权、MVC等)注册逻辑放在下方 }
方案2:EF Core拦截器(仅处理EF发起的数据库请求)
如果仅需要捕获通过EF Core操作触发的冷启动异常,可以用EF Core自带的拦截器:
- 编写自定义拦截器:
using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore.Diagnostics; using System.Data.Common; using System.Threading; using System.Threading.Tasks; public class SqlColdStartInterceptor : DbCommandInterceptor { public override async ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default) { try { return await base.ReaderExecutingAsync(command, eventData, result, cancellationToken); } catch (SqlException ex) when (ex.Number is -2 or 40613) { throw new InvalidOperationException("数据库正在预热,请稍后刷新重试", ex); } } // 按需覆盖NonQueryExecutingAsync、ScalarExecutingAsync等其他执行方法 }
- 在
Startup.cs中注册拦截器到DbContext:
services.AddDbContext<你的DbContext类名>(options => options.UseSqlServer(Configuration.GetConnectionString("你的数据库连接字符串配置名")) .AddInterceptors(new SqlColdStartInterceptor()));
可选优化:从根源降低冷启动异常出现概率
你可以搭配以下配置减少用户遇到该提示的概率:
- 开启Azure App Service的始终开启(Always On)功能,避免应用实例被回收
- 配置EF Core内置的SQL重试策略,自动重试冷启动期间的失败请求:
services.AddDbContext<你的DbContext类名>(options => options.UseSqlServer(Configuration.GetConnectionString("你的数据库连接字符串配置名"), sqlOpt => sqlOpt.EnableRetryOnFailure( maxRetryCount: 3, maxRetryDelay: TimeSpan.FromSeconds(10), errorNumbersToAdd: new[] { -2, 40613 })));
- 配置App Service的应用初始化预热路径,应用启动时主动触发一次轻量数据库查询,提前唤醒SQL实例。
内容的提问来源于stack exchange,提问作者crayden
相关产品推荐
相关产品推荐

