Entity Framework中IQueryable执行Concat报错的解决方法咨询
Entity Framework中IQueryable执行Concat报错的解决方法咨询
你遇到的这个问题并非Entity Framework的bug,而是EF Core的查询翻译规则限制导致的——EF Core要求集合操作(如Concat/Union)必须在最终的客户端投影(Select)之前执行。当你在q1和q2中先执行Select生成DataDefinitionCustom后再合并,EF会判定后续的Concat操作涉及客户端无法翻译的逻辑,因此抛出了那个异常。而手动用UNION ALL在SQL中能正常执行,是因为SQL的集合操作是直接在原始数据层面完成的,和EF的查询翻译逻辑路径不同。
解决思路:将Concat操作提前到Select投影之前
核心就是调整查询结构,先合并两个未经过最终投影的原始查询数据源,再统一执行Select投影。下面是两种具体的实现方案:
方案1:拆分基础查询与投影,合并后统一映射
先分别定义两个查询的基础数据源(只获取需要的字段,不生成DataDefinitionCustom),合并后再统一投影成目标类型:
// 定义q1的基础查询:获取所需的原始字段 var baseQ1 = _diagnosisDataRepository .Entities .Where(ddi => !ddi.DataDefinition.Instances.Any() && ddi.DataDefinition.DataStorageId == id) .Where(did => did.DiagnosisDefinition.Instances.Any()) .Select(ddi => new { DataDefinitionId = ddi.DataDefinition.Id, Instance = ddi.DiagnosisDefinition.Instances.First() }); // 定义q2的基础查询:获取所需的原始字段 var baseQ2 = _dataDefinitionRepository .Entities .Where(dd => dd.DataStorageId == id && dd.Instances != null && dd.Instances.Any()) .Select(dd => new { DataDefinitionId = dd.Id, Instance = dd.Instances.First() }); // 先合并两个基础查询(此时还未做投影,EF可以正常翻译) var combinedQuery = baseQ1.Concat(baseQ2); // 最后统一投影成DataDefinitionCustom var result = combinedQuery .Select(item => new DataDefinitionCustom { DataDefinitionId = item.DataDefinitionId, Instance = item.Instance }) .ToArray();
方案2:直接兼容原始实体结构,合并后统一过滤+投影
如果两个数据源的逻辑可以兼容,也可以直接将原始实体查询转换成统一的匿名结构后合并,再执行投影:
// 转换第一个数据源为统一的匿名类型 var source1 = _diagnosisDataRepository .Entities .Where(ddi => !ddi.DataDefinition.Instances.Any() && ddi.DataDefinition.DataStorageId == id) .Where(did => did.DiagnosisDefinition.Instances.Any()) .Select(ddi => new { DataDefinitionId = ddi.DataDefinition.Id, Instance = ddi.DiagnosisDefinition.Instances.First() }); // 转换第二个数据源为统一的匿名类型 var source2 = _dataDefinitionRepository .Entities .Where(dd => dd.DataStorageId == id && dd.Instances != null && dd.Instances.Any()) .Select(dd => new { DataDefinitionId = dd.Id, Instance = dd.Instances.First() }); // 合并后直接投影成目标类型 var result = source1.Concat(source2) .Select(item => new DataDefinitionCustom { DataDefinitionId = item.DataDefinitionId, Instance = item.Instance }) .ToArray();
额外说明
- 如果你需要查看合并前的SQL语句,直接在
baseQ1或baseQ2上调用ToQueryString()即可,得到的SQL和你之前手动执行的是一致的。 - 这种调整完全符合EF Core的查询翻译规则,不会触发之前的异常,同时也能实现和手动执行
UNION ALL相同的数据库执行效果。
备注:内容来源于stack exchange,提问作者Stefan Dantchev
相关产品推荐
相关产品推荐

