.NET 8 Minimal API更改DBContext作用域后遇MemoryCache已释放错误
问题分析与解决方案
核心问题拆解
- 将DBContext从Singleton改为Scoped后出现
Cannot access disposed object MemoryCache错误:本质并非IMemoryCache被释放(默认Singleton生命周期的服务不会被容器释放),而是AutoMapper映射逻辑与Scoped服务的生命周期冲突——你的AutoMapper配置(Profile)是Singleton,若映射逻辑中错误关联了Scoped的DBContext,请求结束后DBContext被释放,会导致关联操作抛出类似错误(错误信息存在误导)。 - 将IMemoryCache改为Scoped后无报错但映射中读不到缓存:因为Scoped缓存仅在当前请求生命周期内有效,而AutoMapper的Profile是Singleton,初始化时无法获取请求级别的Scoped缓存,后续请求的缓存也无法全局共享。
正确解决方案
1. 恢复IMemoryCache的Singleton生命周期
IMemoryCache默认就是Singleton注册,无需额外修改,确保服务注册中没有错误将其改为Scoped:
// 框架默认注册,无需手动编写 builder.Services.AddMemoryCache();
2. 修正AutoMapper读取缓存的方式
AutoMapper的Profile是Singleton,不能直接在构造函数中依赖Scoped服务,读取缓存的逻辑需通过自定义ValueResolver实现,确保依赖注入的正确性:
步骤1:创建缓存读取的ValueResolver
public class UpdatesCacheResolver : IValueResolver<SourceModel, DestinationModel, List<YourUpdateType>> { private readonly IMemoryCache _memoryCache; // 注入Singleton的IMemoryCache public UpdatesCacheResolver(IMemoryCache memoryCache) { _memoryCache = memoryCache; } public List<YourUpdateType> Resolve(SourceModel source, DestinationModel destination, List<YourUpdateType> destMember, ResolutionContext context) { if (_memoryCache.TryGetValue("Updates", out List<YourUpdateType> updates)) { return updates; } // 缓存不存在时返回默认值 return new List<YourUpdateType>(); } }
步骤2:在Profile中注册Resolver
public class YourMappingProfile : Profile { public YourMappingProfile() { CreateMap<SourceModel, DestinationModel>() .ForMember(dest => dest.TargetField, opt => opt.MapFrom<UpdatesCacheResolver>()); } }
步骤3:注册Resolver为Singleton
builder.Services.AddSingleton<UpdatesCacheResolver>(); // 确保AutoMapper正常注册 builder.Services.AddAutoMapper(typeof(YourMappingProfile));
3. 优化API接口的请求体处理
无需手动读取Stream,直接用[FromBody]绑定模型,简化代码并避免潜在流处理问题:
public static async Task<Results<Accepted, ProblemHttpResult>> GetUpdates( [FromServices] IMemoryCache memoryCache, [FromBody] List<YourUpdateType> list) // 自动绑定请求体到模型 { try { memoryCache.Set("Updates", list, new MemoryCacheEntryOptions { // 可根据需求设置缓存过期时间 AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1) }); return TypedResults.Accepted("/"); } catch (Exception ex) { return TypedResults.Problem(ex.Message); } }
4. 验证Scoped服务的使用范围
确保所有依赖DBContext(Scoped)的操作都在请求生命周期内完成,避免在请求结束后(Scope已释放)执行异步操作访问Scoped服务。
内容的提问来源于stack exchange,提问作者Kazem Ma
相关产品推荐
相关产品推荐

