ASP.NET应用添加带数据库连接的WebSocket遇依赖注入问题
问题分析与解决方案
你的核心问题出在中间件生命周期与Scoped服务的冲突:ASP.NET中间件的构造函数是在应用启动阶段由根服务提供者解析的,而DatabaseService是Scoped生命周期(每个请求创建实例),根提供者无法直接解析Scoped服务;同时手动注册中间件为Scoped时,又无法自动注入RequestDelegate(中间件链的固定参数)。
解决步骤
1. 修改WebSocketMiddleware类
移除构造函数中的DatabaseService注入,改为在请求执行阶段通过HttpContext获取Scoped服务:
public class WebSocketMiddleware { private readonly RequestDelegate next; // 仅保留RequestDelegate参数,符合中间件构造规范 public WebSocketMiddleware(RequestDelegate next) { this.next = next; } public async Task Invoke(HttpContext context) { if (context.WebSockets.IsWebSocketRequest) { // 从当前请求的服务容器中获取Scoped的DatabaseService var databaseService = context.RequestServices.GetRequiredService<DatabaseService>(); WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); await HandleWebSocketConnection(context, webSocket, databaseService); } else { Console.WriteLine("Not ws"); await next(context); } } // 新增DatabaseService参数,在方法内直接使用 private async Task HandleWebSocketConnection(HttpContext context, WebSocket webSocket, DatabaseService databaseService) { // 在这里正常调用databaseService的数据库操作方法 // 示例:var data = await databaseService.GetRecordsAsync(); // ... 其他WebSocket消息处理逻辑 } }
2. 修正Program.cs中的服务注册
移除手动注册WebSocketMiddleware的代码,因为ASP.NET框架会通过UseMiddleware<T>自动处理中间件的实例化:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext<DatabaseContext>(options => options.UseNpgsql("address")); builder.Services.AddScoped<DatabaseService>(); // 删除这一行:builder.Services.AddScoped<WebSocketMiddleware>(); builder.Services.AddCors(options => { options.AddPolicy("AllowLocalhost", builder => { builder.WithOrigins("https://localhost:3000") .AllowAnyHeader() .AllowCredentials() .AllowAnyMethod(); }); }); var app = builder.Build(); app.UseCors("AllowLocalhost"); app.UseWebSockets(); app.UseMiddleware<WebSocketMiddleware>(); // 保留该行,框架自动处理中间件构造 app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
3. 保留DatabaseService的原有实现
你的DatabaseService和DatabaseContext的依赖注入配置是正确的,无需修改:
public class DatabaseService { private readonly DatabaseContext databaseContext; public DatabaseService(DatabaseContext databaseContext) { this.databaseContext = databaseContext; } // 你的数据库操作方法 }
原理说明
- 中间件构造函数仅在应用启动时执行一次,属于根生命周期,不能直接依赖Scoped服务;而
Invoke方法是每个请求都会执行,通过context.RequestServices可以获取当前请求的Scoped服务实例,完全符合依赖注入的生命周期规则。 UseMiddleware<T>会自动处理中间件的实例化,并注入RequestDelegate参数,不需要手动将中间件注册到DI容器。
内容的提问来源于stack exchange,提问作者eskil4152
相关产品推荐
相关产品推荐

