ASP.NET Core 6 MVC启动时缓存数据库数据遇依赖注入错误求助
问题分析与解决方案
错误原因
你当前的问题出在直接使用根IServiceProvider获取Scoped生命周期的服务:
IAccountManagerRepository通常注册为Scoped(与请求生命周期绑定),而根服务容器不负责管理Scoped服务的创建与释放,直接调用GetService会导致解析失败。IStartupFilter的执行时机是在应用管道构建阶段,此时服务容器尚未完全初始化,无法正确处理Scoped服务的依赖链。
解决方案一:修改中间件以支持请求级服务解析
如果你的需求是每个请求都执行数据获取与缓存更新,修改中间件的Invoke方法,通过HttpContext.RequestServices(请求级服务提供者)来获取服务:
public class RequestSetOptionsMiddleware { private readonly RequestDelegate _next; // 移除IServiceProvider注入,直接在Invoke中使用HttpContext的服务 public RequestSetOptionsMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext) { Console.WriteLine("test 12312 1 23 123 1 23 12"); // 创建请求级Scope,确保Scoped服务被正确管理 using var scope = httpContext.RequestServices.CreateScope(); var cont = scope.ServiceProvider.GetRequiredService<IAccountManagerRepository>(); var accountManagers = cont.GetAccountManagersThirdPartyNotTrueWithSelection(); // 这里添加缓存逻辑,比如存入IMemoryCache var cache = scope.ServiceProvider.GetRequiredService<IMemoryCache>(); cache.Set("AccountManagers", accountManagers); await _next(httpContext); } } public class RequestSetOptionsStartupFilter : IStartupFilter { public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) { return builder => { builder.UseMiddleware<RequestSetOptionsMiddleware>(); next(builder); }; } }
解决方案二:用IHostedService实现启动时一次性预加载缓存
如果你的需求是应用启动时仅执行一次数据获取并缓存,IStartupFilter不是合适的选择,应该使用IHostedService(后台服务),它会在应用完全启动后执行:
1. 实现后台服务
public class CachePreloadHostedService : IHostedService { private readonly IServiceProvider _serviceProvider; private readonly IMemoryCache _cache; public CachePreloadHostedService(IServiceProvider serviceProvider, IMemoryCache cache) { _serviceProvider = serviceProvider; _cache = cache; } public async Task StartAsync(CancellationToken cancellationToken) { // 创建Scope解析Scoped仓储服务 using var scope = _serviceProvider.CreateScope(); var repo = scope.ServiceProvider.GetRequiredService<IAccountManagerRepository>(); // 建议使用异步方法避免阻塞启动 var accountManagers = await repo.GetAccountManagersThirdPartyNotTrueWithSelectionAsync(); // 将数据存入缓存,设置过期时间 _cache.Set("AccountManagers", accountManagers, TimeSpan.FromHours(1)); } public Task StopAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } }
2. 在Program.cs注册服务
// 注册缓存服务(如果还没注册) builder.Services.AddMemoryCache(); // 注册后台服务 builder.Services.AddHostedService<CachePreloadHostedService>();
关键注意事项
- 永远不要用根
IServiceProvider获取Scoped服务,必须通过CreateScope创建请求级或临时Scope来解析。 - 区分启动时一次性执行和每个请求执行的场景,选择对应的实现方式:
IHostedService适合前者,中间件适合后者。
内容的提问来源于stack exchange,提问作者developerkhashab
相关产品推荐
相关产品推荐

