缓存场景下Object Disposed异常问题求助
我写了一个Cache.cs类,能在指定时长内缓存对象,缓存过期时会通过委托刷新缓存数据。
比如我有个DatumService类,用来存储Datum表的所有行数据。因为这张表几乎不会变更,所以可以安全存在内存里,我设置的缓存过期时间是1小时。
1小时后会调用下面的方法刷新缓存:
/// <summary> /// Gets all Datum records to be cached. /// </summary> private List<Datum> GetDatums() { return ModelContext .Datums .AsNoTracking() .ToList(); }
这个方法通过Entity Framework重新填充缓存里的Datum数据。
现在遇到的问题和ModelContext的实现有关:DatumService继承自ServiceBase类,这个类注入了IServiceProvider:
private IServiceProvider _serviceProvider { get; set; } = default!; public ServiceBase(IServiceProvider provider) { _serviceProvider = provider; } public ModelContext? ModelContext => _serviceProvider.GetService<ModelContext>();
之前我直接注入ModelContext也会碰到同样的问题。
ModelContext来自微软依赖注入容器,当缓存过期调用刷新方法时,IServiceProvider会抛出“Object Disposed”异常,该怎么解决?
避免生命周期不匹配:
ModelContext默认是Scoped生命周期,和请求绑定,请求结束后会被容器释放。如果DatumService是Singleton(缓存类通常是单例),直接持有或通过IServiceProvider获取的ModelContext会在请求结束后被释放,后续调用必然抛出已释放异常。用
IServiceScopeFactory创建临时作用域:在ServiceBase中注入IServiceScopeFactory替代IServiceProvider,每次需要使用ModelContext时,创建新的作用域,用完自动释放:
private IServiceScopeFactory _scopeFactory { get; set; } = default!; public ServiceBase(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } private List<Datum> GetDatums() { using var scope = _scopeFactory.CreateScope(); var context = scope.ServiceProvider.GetRequiredService<ModelContext>(); return context.Datums.AsNoTracking().ToList(); }
每次刷新缓存时都会生成全新的ModelContext实例,用完后通过using自动释放,彻底避免已释放问题。
校验服务生命周期配置:确保
DatumService如果是Singleton,注入的服务都是支持Singleton依赖的类型(比如IServiceScopeFactory本身就是Singleton,无冲突)。绝对不要直接将Scoped的ModelContext注入到Singleton服务中,这是DI容器的典型禁忌。不推荐的备选方案:虽然可以把
ModelContext注册为Singleton,但EF的DbContext并非线程安全,会引发并发问题和数据一致性问题,除非你能保证缓存刷新操作是严格单线程执行,否则不要这么做。
内容的提问来源于stack exchange,提问作者Paul Sinnema

