问题描述
当项目中使用 IServiceCollection.AddFxServices() 这类通过 Castle.DynamicProxy 为业务服务添加 AOP 支持的方法时,EFCore.Sharding 会导致数据库连接池迅速耗尽,最终抛出 System.InvalidOperationException: Timeout expired 异常。
环境
.NET 版本:.NET 6/8
EFCore.Sharding 版本:x.x.x
数据库:SQL Server
根本原因分析
生命周期冲突:EFCore.Sharding 框架内部疑似将 DbContext 注册或缓存为 Singleton 或长生命周期对象。当它被注入到 Scoped 的业务服务中时,由于生命周期不匹配,DbContext 无法在请求结束后被正确回收,导致连接泄漏。
代理加剧泄漏:项目使用了动态代理(AOP)为 Scoped 服务创建代理。如果代理工厂(如 ProxyGenerator)是 Singleton,它将长期持有 Scoped 服务的引用,进一步阻止了服务和 DbContext 被垃圾回收,使得连接泄漏问题迅速暴露。
验证证据:在移除动态代理的 AOP 功能后,问题消失,说明代理的介入是触发此 Bug 的必要条件。
建议修复方案
方案一(框架层面):审查框架内部所有 DbContext 实例的注册和缓存逻辑,确保其生命周期为 Scoped,并遵循 .NET Core DI 的最佳实践。
方案二(用户层面):在文档中增加说明,尤其是在推荐与 AOP 框架(如 Castle.DynamicProxy、Autofac 等)集成时,应特别注意不要将任何与数据库操作相关的服务注册为 Singleton。
问题描述
当项目中使用 IServiceCollection.AddFxServices() 这类通过 Castle.DynamicProxy 为业务服务添加 AOP 支持的方法时,EFCore.Sharding 会导致数据库连接池迅速耗尽,最终抛出 System.InvalidOperationException: Timeout expired 异常。
环境
.NET 版本:.NET 6/8
EFCore.Sharding 版本:x.x.x
数据库:SQL Server
根本原因分析
生命周期冲突:EFCore.Sharding 框架内部疑似将 DbContext 注册或缓存为 Singleton 或长生命周期对象。当它被注入到 Scoped 的业务服务中时,由于生命周期不匹配,DbContext 无法在请求结束后被正确回收,导致连接泄漏。
代理加剧泄漏:项目使用了动态代理(AOP)为 Scoped 服务创建代理。如果代理工厂(如 ProxyGenerator)是 Singleton,它将长期持有 Scoped 服务的引用,进一步阻止了服务和 DbContext 被垃圾回收,使得连接泄漏问题迅速暴露。
验证证据:在移除动态代理的 AOP 功能后,问题消失,说明代理的介入是触发此 Bug 的必要条件。
建议修复方案
方案一(框架层面):审查框架内部所有 DbContext 实例的注册和缓存逻辑,确保其生命周期为 Scoped,并遵循 .NET Core DI 的最佳实践。
方案二(用户层面):在文档中增加说明,尤其是在推荐与 AOP 框架(如 Castle.DynamicProxy、Autofac 等)集成时,应特别注意不要将任何与数据库操作相关的服务注册为 Singleton。