Linq-To-Entities查询异常环境差异求助:无法创建常量值
问题原因拆解
首先纠正你的误解:这个错误不是延迟执行导致的,而是EF在处理投影(select new)中的跨DbSet子查询时,不同版本的解析逻辑不一样。
1. 错误本身的本质
你代码里的Video = ctx.tblVideo_Questao_Simulado.Any(...)是在投影对象的属性中直接引用了另一个DbSet的查询。在早期的EF版本(搭配旧编译器)中,EF可能会“宽松”地将这个表达式转换成对应的SQL EXISTS子查询;但在更高版本的EF(搭配新编译器)中,EF的表达式树解析器变得更严格,它会认为你在试图创建一个tblVideo_Questao_Simulado类型的常量值(而EF只支持基元/枚举类型的常量),从而抛出NotSupportedException。
2. VS2012 vs VS2017的核心差异
这两个版本的差异主要来自两方面:
- C#编译器版本:VS2012用的是C# 5.0,VS2017是C# 7.x。新版本的编译器生成的表达式树结构更严谨,EF对这种新的表达式树解析时,不会再兼容旧的“不规范”写法。
- Entity Framework版本:VS2012默认通常搭配EF5或EF6的早期版本,而VS2017默认的EF版本(比如EF6.1及以上)修复了很多旧版本的解析漏洞,对Linq-to-Entities的语法规则执行更严格,之前能“侥幸”运行的代码就会触发正确的异常。
修复方案
最直接的修复是把跨DbSet的子查询提前到投影之外,用let子句先计算结果,再在投影中引用:
public QuestaoSimulado GetByID(int idQuestao) { var query = from questao in ctx.tblQuestoes where questao.intQuestaoID == idQuestao // 先把视频存在性的判断抽出来 let hasAssociatedVideo = ctx.tblVideo_Questao_Simulado.Any(v => v.intQuestaoID == questao.intQuestaoID) select new QuestaoSimulado() { Enunciado = questao.txtEnunciado, Comentario = questao.txtComentario, Id = questao.intQuestaoID, Ano = questao.intYear ?? 0, Tipo = questao.bitCasoClinico == "1" ? (int)Questao.tipoQuestao.DISCURSIVA : (int)Questao.tipoQuestao.OBJETIVA, Video = hasAssociatedVideo }; return query.FirstOrDefault(); }
如果你的实体类之间有导航属性(比如tblQuestoes里有ICollection<tblVideo_Questao_Simulado> Videos),那写法可以更简洁,直接用questao.Videos.Any(),这样EF能更清晰地解析关联关系,不会出现这个问题。
总结
本质上是新版本的C#编译器和EF对Linq表达式的校验更严格了,旧版本中被“兼容”的不规范写法,在新版本中被正确识别为不符合规则的代码。通过调整查询结构,让EF能正确解析子查询的意图,就能解决这个问题。
内容的提问来源于stack exchange,提问作者Stormhashe
相关产品推荐
相关产品推荐

