EF Core生成SQL为何用CASE而非WHERE子句?如何调整解析器?
问题根因
生成低效带CASE语句SQL的核心原因是表达式构建时用错了组合方法:
你代码中使用的Expression.And、Expression.Or是生成按位与/按位或整数运算的方法,不是生成布尔逻辑与/或的方法。
EF Core翻译时,遇到两个布尔值做按位或|运算,无法直接映射为SQL的OR逻辑关键字,只能先把每个布尔判断结果转换为1/0的bit值(即你看到的CASE块:条件成立返回1,否则返回0),再对两个bit值做按位或运算,最后判断运算结果等于1,最终就生成了你看到的冗余SQL。
修复方法
只需要把按位运算方法替换为对应的布尔逻辑运算方法,同时修正代码里的几处书写错误即可:
- 用
Expression.AndAlso(对应C#的逻辑与&&,可直接翻译为SQL的AND)替换Expression.And - 用
Expression.OrElse(对应C#的逻辑或||,可直接翻译为SQL的OR)替换Expression.Or - 修正原代码中取
value属性的错误写法(原代码写反了调用顺序,会直接抛运行时错误) - 补全缺失的语句分号,增加简单类型转换逻辑,避免常量值和属性类型不匹配
修正后的核心解析方法代码如下:
private Expression? ParseTree<T>(JsonElement condition, ParameterExpression parameterExpression) { Expression? left = null; var conditionString = condition.GetProperty("condition").GetString()?.ToUpper(); if (!Enum.TryParse(conditionString, out FilterConditionType gate)) { throw new ArgumentOutOfRangeException(nameof(condition), $"Not expected condition type: {conditionString}."); } JsonElement rules = condition.GetProperty("rules"); // 绑定正确的逻辑运算方法 Binder binder = gate == FilterConditionType.AND ? Expression.AndAlso : Expression.OrElse; Expression? bind(Expression? left, Expression? right) => left == null ? right : binder(left, right); foreach (var rule in rules.EnumerateArray()) { string? column = rule.GetProperty("column").GetString(); // 修正原代码value取值错误 object? rawValue = rule.GetProperty("value").GetString(); Expression property = Expression.Property(parameterExpression, column); // 自动转换常量类型匹配属性类型,避免类型不匹配报错 Expression constant = Expression.Constant(Convert.ChangeType(rawValue, property.Type)); BinaryExpression equalExp = Expression.Equal(property, constant); left = bind(left, equalExp); } return left; }
修改后重新生成的表达式会被EF Core正确识别为标准布尔逻辑判断,直接翻译为你预期的WHERE [c].[Foo] = N'Bar' OR [c].[Foo] = N'Baz'格式SQL,不会再产生冗余的CASE和按位运算逻辑,查询效率会回到正常水平。
内容的提问来源于stack exchange,提问作者DarkLeafyGreen
相关产品推荐
相关产品推荐

