EF Core LINQ查询报'A command is already in progress'错误解决
问题原因
你的代码触发异常+无法生成单条SQL的核心原因有两个:
- 你将
IQueryable类型的DbSet强转为IEnumerable<IPosition>,这会强制EF Core停止后续的SQL翻译,所有Concat、Join、Where操作都会在客户端内存中执行。Npgsql默认不支持单连接上同时存在多个执行中的命令,当EF Core在枚举第一个查询结果的过程中发起第二个查询时,就会抛出A command is already in progress异常,修改DbContext生命周期为Transient无法解决这个问题。 - 你使用接口类型
IPosition作为Concat的统一类型,EF Core的查询翻译器不支持基于接口类型的投影翻译,会直接回退到客户端评估。
另外你原始代码的JOIN条件存在笔误:equals右值的BetCategory错误引用了左表x的字段,会导致关联逻辑完全错误。
修复代码
全程保持IQueryable类型,使用结构一致的匿名类型做联合投影,EF Core可以正常将整个逻辑翻译为单条SQL执行:
// 分别投影为结构完全一致的匿名类型,保持IQueryable特性 var dkRows = dbContext.DraftkingsPositions .Select(p => new { p.EventKey, p.CustomParticipant, p.BetCategory, p.CustomLine, p.PositionId, p.OddsDecimal, p.BetPosition, IsFanduel = false }); var fdRows = dbContext.FanduelPositions .Select(p => new { p.EventKey, p.CustomParticipant, p.BetCategory, p.CustomLine, p.PositionId, p.OddsDecimal, p.BetPosition, IsFanduel = true }); // 联合为单查询,翻译为SQL的UNION ALL var combined = dkRows.Concat(fdRows); // 关联时直接过滤来源,避免同平台数据无效匹配,修正原JOIN条件笔误 var arbitragePositions = from fd in combined.Where(r => r.IsFanduel) join dk in combined.Where(r => !r.IsFanduel) on new { fd.EventKey, fd.CustomParticipant, fd.BetCategory, fd.CustomLine } equals new { dk.EventKey, dk.CustomParticipant, dk.BetCategory, dk.CustomLine } where fd.BetPosition != dk.BetPosition && (1 / fd.OddsDecimal) + (1 / dk.OddsDecimal) < 0.985 select new { fdPos = fd.PositionId, fdOdds = fd.OddsDecimal, fdbetPos = fd.BetPosition, dkPos = dk.PositionId, dkOdds = dk.OddsDecimal, dkbetPos = dk.BetPosition }; // 单次执行,生成单条SQL return Ok(arbitragePositions.ToList());
效果说明
- 上述写法EF Core会生成单条带UNION ALL的JOIN语句,所有计算、过滤逻辑都在数据库侧完成,执行性能和你手写的原生SQL基本一致,可以达到150ms左右的耗时
- 没有客户端评估逻辑,不会出现在结果枚举过程中发起新查询的场景,从根源上规避Npgsql的并发命令异常
- 提前通过
IsFanduel标记拆分关联的两侧数据,避免了原写法全量自关联产生的笛卡尔积无效计算,实际执行效率比你原本预期的逻辑更高
内容的提问来源于stack exchange,提问作者user3533755
相关产品推荐
相关产品推荐

