EF Core AddDbContextPool报错后自动分离附加实体方案
问题背景
现有代码与配置
- DbContext池化注册代码,使用
AddDbContextPool实现实例池化复用以提升性能:
services.AddDbContextPool<SecurityDBContext>(options => options.UseSqlServer(GlobalConfig.Configuration["ConnectionStrings:DefaultConnection"], b => b.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)) );
- 实体新增业务代码:
db.Entry(new AdminBlockClientConfig() { ActionId = input.aid.ToLongReturnZiro(), MaxValue = input.value.ToIntReturnZiro(), IsActive = input.isActive.ToBooleanReturnFalse(), SiteSettingId = siteSettingId.ToIntReturnZiro() }).State = EntityState.Added; db.SaveChanges();
- 业务服务构造函数定义:
readonly SecurityDBContext db = null; static List<AdminBlockClientConfig> AdminBlockClientConfigs = null; public AdminBlockClientConfigService(SecurityDBContext db) { this.db = db; }
- 业务服务依赖注入配置:
services.AddScoped<IAdminBlockClientConfigService, AdminBlockClientConfigService>();
核心问题
新增实体逻辑未做字段合法性校验时,若传入非法值(例如ActionId=-1违反外键约束),会正常抛出数据库插入异常,但异常抛出后SecurityDBContext实例会被污染:出错的实体仍保持附加在上下文的状态,后续该上下文实例上的所有SaveChanges调用都会失败。该问题和AddDbContextPool本身无关,只要任意服务向上下文添加了非法数据,后续复用该实例的所有服务都无法正常使用。
诉求
- 实现操作出错后自动从DbContext分离出错实体,保证上下文可继续正常使用
- 不采用手动逐处补全校验的方案:现有待补校验体量太大,手动补全遗漏风险高
- 不调整现有
AddDbContextPool注册配置,保留池化带来的性能收益
解决方案
无需修改现有DbContext池化配置,也不需要逐处补全业务校验,直接在SecurityDBContext类中重写SaveChanges和SaveChangesAsync方法,统一捕获数据库更新异常,自动分离出错实体即可,全程无业务代码侵入。
直接将以下代码添加到SecurityDBContext类中:
public override int SaveChanges(bool acceptAllChangesOnSuccess) { try { return base.SaveChanges(acceptAllChangesOnSuccess); } catch (DbUpdateException ex) { // 分离所有触发更新异常的实体,解除上下文污染 foreach (var entry in ex.Entries) { entry.State = EntityState.Detached; } // 保留原异常抛出逻辑,不影响上层错误处理 throw; } } public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) { try { return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); } catch (DbUpdateException ex) { // 分离所有触发更新异常的实体,解除上下文污染 foreach (var entry in ex.Entries) { entry.State = EntityState.Detached; } // 保留原异常抛出逻辑,不影响上层错误处理 throw; } }
方案特性
- 零业务代码侵入:所有现有SaveChanges调用自动适配,不需要逐处修改业务逻辑
- 清理精度可控:仅分离实际触发更新异常的实体条目,不会清除上下文中其他正常跟踪的待提交数据,不影响正常业务流程
- 完全兼容DbContext池:异常触发后上下文会立即恢复可用状态,归还到池后后续复用该实例的请求不会被污染,完全保留池化的性能收益
- 不影响原有异常处理:异常会按原逻辑继续向上抛出,上层的错误日志、用户提示等逻辑完全不受影响
如果遇到极端场景下DbUpdateException.Entries未覆盖所有关联的异常跟踪实体,可以在catch块中追加兜底逻辑,遍历ChangeTracker中所有非Unchanged状态的条目做合法性校验,分离校验不通过的实体即可。常规的外键冲突、字段约束冲突等场景下,上述基础实现已经可以完全覆盖。
内容的提问来源于stack exchange,提问作者testApplication Insurance
相关产品推荐
相关产品推荐

