C产品如何满足特定用户需求?
- 内容介绍
- 文章标签
- 相关推荐
本文共计897个文字,预计阅读时间需要4分钟。
Expression 不是高级的 Func,它是用来使代码变成可分析、可翻译、可截取的数据结构;传递给 EF Core 的 Where() 时,你基本上不需要 Compile(),调用了反而无效。
EF Core 中传 Expression> 却没走 SQL?查 Client evaluation 日志
EF Core 看到自定义方法(比如 MyUtil.IsAdult(x))或未注册的静态函数,会直接放弃翻译,退化为客户端求值:先把全表拉到内存,再用 Where() 过滤。这不是 bug,是默认行为。
- 打开 EF Core 日志,搜索
Client evaluation—— 只要出现,就说明表达式被跳过了 - 只信任内置方法:
string.Contains、DateTime.Now、Math.Abs等;其他一律视为不可翻译 - 想用自定义函数进 SQL,必须显式注册:
ModelBuilder.HasDbFunction()+ 原生 SQL 实现 - 简单条件优先用
Expression.Equal、Expression.AndAlso拼,别封装成独立方法再Expression.Call
ParameterExpression 必须复用同一个实例,不是名字一样就行
手动构建表达式树时,ParameterExpression 是入口,但它的 Name 属性只是调试用,真正绑定靠的是引用相等。两个同名、同类型的 ParameterExpression 实例,在运行时会被视为完全无关的变量。
- 错误写法:
var p1 = Expression.Parameter(typeof(User), "u"); var p2 = Expression.Parameter(typeof(User), "u");→ 后续用p2构建 body,再用p1创建LambdaExpression,直接抛InvalidOperationException: variable 'u' ... not defined - 正确做法:统一声明
var param = Expression.Parameter(typeof(User), "u");,所有Expression.Property(param, "Name")、Expression.Equal(...)都基于它 - 跨模块组合子表达式(比如用
Expression.Invoke()或手动AndAlso)时,也必须共用这一个param实例,否则 EF Core 翻译 SQL 时丢参数
Expression.Compile() 不是必需操作,高频调用下反而是性能雷区
Compile() 是把表达式树转成 IL 的过程,每次调用都触发 JIT 编译。在 Web 请求级动态拼条件时反复 Compile(),比硬编码 x => x.Status == "A" 慢 10 倍以上。
- EF Core、Dapper(配合解析器)、LINQ to Objects 场景下,你本就该传
Expression<func bool>></func>,框架自己遍历翻译或执行;Compile()会让 EF Core 失去服务端能力,强制走内存过滤 - 只在脱离 IQueryable、且结构稳定需复用委托时才缓存一次,例如通用校验器、规则引擎策略
- 缓存推荐用
ConcurrentDictionary<string delegate></string>或Lazy<func bool>></func>,注意泛型擦除:Expression<func bool>></func>和Expression<func bool>></func>编译后是不同委托类型,不能混 key
最常被忽略的一点:表达式树是不可变的,所有节点必须从叶到根构造;一旦写错参数引用或提前 Compile(),问题往往不报错,而是静默失效——查日志、盯参数实例、别信“名字对就行”。
本文共计897个文字,预计阅读时间需要4分钟。
Expression 不是高级的 Func,它是用来使代码变成可分析、可翻译、可截取的数据结构;传递给 EF Core 的 Where() 时,你基本上不需要 Compile(),调用了反而无效。
EF Core 中传 Expression> 却没走 SQL?查 Client evaluation 日志
EF Core 看到自定义方法(比如 MyUtil.IsAdult(x))或未注册的静态函数,会直接放弃翻译,退化为客户端求值:先把全表拉到内存,再用 Where() 过滤。这不是 bug,是默认行为。
- 打开 EF Core 日志,搜索
Client evaluation—— 只要出现,就说明表达式被跳过了 - 只信任内置方法:
string.Contains、DateTime.Now、Math.Abs等;其他一律视为不可翻译 - 想用自定义函数进 SQL,必须显式注册:
ModelBuilder.HasDbFunction()+ 原生 SQL 实现 - 简单条件优先用
Expression.Equal、Expression.AndAlso拼,别封装成独立方法再Expression.Call
ParameterExpression 必须复用同一个实例,不是名字一样就行
手动构建表达式树时,ParameterExpression 是入口,但它的 Name 属性只是调试用,真正绑定靠的是引用相等。两个同名、同类型的 ParameterExpression 实例,在运行时会被视为完全无关的变量。
- 错误写法:
var p1 = Expression.Parameter(typeof(User), "u"); var p2 = Expression.Parameter(typeof(User), "u");→ 后续用p2构建 body,再用p1创建LambdaExpression,直接抛InvalidOperationException: variable 'u' ... not defined - 正确做法:统一声明
var param = Expression.Parameter(typeof(User), "u");,所有Expression.Property(param, "Name")、Expression.Equal(...)都基于它 - 跨模块组合子表达式(比如用
Expression.Invoke()或手动AndAlso)时,也必须共用这一个param实例,否则 EF Core 翻译 SQL 时丢参数
Expression.Compile() 不是必需操作,高频调用下反而是性能雷区
Compile() 是把表达式树转成 IL 的过程,每次调用都触发 JIT 编译。在 Web 请求级动态拼条件时反复 Compile(),比硬编码 x => x.Status == "A" 慢 10 倍以上。
- EF Core、Dapper(配合解析器)、LINQ to Objects 场景下,你本就该传
Expression<func bool>></func>,框架自己遍历翻译或执行;Compile()会让 EF Core 失去服务端能力,强制走内存过滤 - 只在脱离 IQueryable、且结构稳定需复用委托时才缓存一次,例如通用校验器、规则引擎策略
- 缓存推荐用
ConcurrentDictionary<string delegate></string>或Lazy<func bool>></func>,注意泛型擦除:Expression<func bool>></func>和Expression<func bool>></func>编译后是不同委托类型,不能混 key
最常被忽略的一点:表达式树是不可变的,所有节点必须从叶到根构造;一旦写错参数引用或提前 Compile(),问题往往不报错,而是静默失效——查日志、盯参数实例、别信“名字对就行”。

