ASP.NET 5结合EF6使用Autofac子生命周期域时对象未释放问题
1. 子生命周期域使用逻辑错误
你代码中虽然通过itemScope.BeginLifetimeScope()创建了新的子生命周期域scope,但后续解析ISomeService时仍然使用的是父域itemScope,完全没有用到新创建的子域。所有服务实例依然是从父域解析得到,只要父域没有被释放,所有解析得到的可释放实例(包括UnitOfWork、EF数据上下文)都不会被销毁,EF上下文自然会持续持有跟踪的SomeObject实体,导致内存引用持续上涨。
2. Autofac 可释放瞬时实例的追踪机制
就算你后续修正了解析域的问题,你当前将UnitOfWork和服务注册为InstancePerDependency(瞬时实例)的模式也存在风险:Autofac会自动追踪所有实现了IDisposable接口的实例,哪怕是瞬时实例,也会被绑定到解析它的生命周期域上,只有域释放时才会调用Dispose方法。如果你的UnitOfWork内部持有EF数据上下文,只要UnitOfWork不被释放,EF上下文就会一直存活,持续持有跟踪的实体。
1. 修正子域解析逻辑
将解析服务的源从父域itemScope改为你创建的子域scope:
//Save object of type SomeObject using (var scope = itemScope.BeginLifetimeScope()) { // 改为从新的子域解析服务 var someService = scope.Resolve<ISomeService>(); var savedObject = await someService.SaveAsync(objectToSave); }
子域在using代码块结束后会自动释放,所有从该子域解析的可释放实例都会被同步销毁。
2. 优化服务注册配置
将UnitOfWork和相关服务的生命周期调整为按生命周期域实例化,更适配这种每次循环新建域的场景:
builder.RegisterType<UnitOfWork>() .As(typeof(IUnitOfWork)) // 每个生命周期域只创建一个实例 .InstancePerLifetimeScope(); builder.RegisterAssemblyTypes(assemblies) .Where(t => t.FullName.StartsWith("MyApp") && t.Name.EndsWith("Service")) .AsImplementedInterfaces() .InstancePerLifetimeScope();
同时确保你的EF数据上下文也注册为InstancePerLifetimeScope,保证每个子域拿到的都是全新的上下文,子域销毁时上下文同步释放,所有跟踪的实体都会被解除引用。
3. EF 层面补充优化(可选)
如果你的保存逻辑不需要后续跟踪实体,可以做以下配置进一步降低内存占用:
- 保存操作对应的查询添加
AsNoTracking()扩展,关闭实体跟踪 - 保存完成后手动调用
DbContext.Entry(entity).State = EntityState.Detached解除实体跟踪 - 对于只读场景可以全局关闭EF上下文的自动跟踪功能
内容的提问来源于stack exchange,提问作者Ivan-Mark Debono

