Expression.Compile() 不应频繁调用,因其每次都会触发IL生成、JIT编译和委托创建,导致高CPU、GC压力及内存泄漏;正确做法是通过结构化key在ConcurrentDictionary或IMemoryCache中缓存编译结果。

Expression.Compile() 为什么不能每次都调用
每次调用 Expression.Compile() 都会触发 IL 生成、JIT 编译和委托创建,底层涉及 DynamicMethod 或 AssemblyBuilder(.NET Core/5+ 默认走 DynamicMethod),实际开销远高于普通方法调用。在高并发场景下,反复编译同一表达式会导致 CPU 突增、GC 压力上升,甚至触发 JIT 线程争用。
典型错误模式是:在循环里或高频请求中对相同结构的 Expression 反复 Compile(),比如动态构建查询条件时没做缓存。
- 编译一次平均耗时约 0.1–1ms(取决于表达式复杂度),但并发 100 线程同时编译同一表达式,可能产生上百个等效但互不共享的委托实例
- 生成的委托对象不可回收——它们被 JIT 和运行时内部强引用,长期驻留内存
- .NET 6+ 对简单表达式有轻微优化(如常量折叠),但不改变编译本质开销
手动缓存 Expression.Compile() 结果的正确姿势
缓存核心是「键唯一性 + 线程安全」。不能只用 Expression.ToString() 当 key——它不稳定(节点顺序、调试信息可能变),也不反映语义等价性。推荐用表达式结构哈希或标准化后比较。
最简可行方案是用 ConcurrentDictionary,key 由表达式类型 + 关键参数组合生成(例如字段名、操作符、字面值):
系统优势: 1、 使用全新ASP.Net+c#和三层结构开发. 2、 可生成各类静态页面(html,htm,shtm,shtml和.aspx) 3、 管理后台风格模板自由选择,界面精美 4、 风格模板每月更新多套,还可按需定制 5、 独具的缓存技术加快网页浏览速度 6、 智能销售统计,图表分析 7、 集成国内各大统计系统 8、 多国语言支持,内置简体繁体和英语 9、 UTF-8编码,可使用于全球
private static readonly ConcurrentDictionary> _cache = new(); public static Func
GetFilter(string propertyName, object value) { var key = $"Person_{propertyName}Equals{value}"; return cache.GetOrAdd(key, => { var param = Expression.Parameter(typeof(Person)); var body = Expression.Equal( Expression.Property(param, propertyName), Expression.Constant(value) ); return Expression.Lambda >(body, param).Compile(); }); }
- 避免把整个
Expression对象当 key(它没重写GetHashCode,默认是引用哈希) - 不要在 lambda 捕获外部变量(如
value是局部变量),否则闭包会阻止委托被缓存复用 - 若表达式含
Expression.Constant(new object()),该对象每次新建,导致 key 不一致——应提前提取为静态只读字段
Expression.Compile() 在 ASP.NET Core 中的常见误用
在控制器或中间件里动态构建并编译表达式(如基于查询字符串生成过滤器),极易成为性能瓶颈点。框架本身不帮你缓存,全靠自己设计生命周期。
- Scoped 服务里缓存需注意:Scoped 实例随请求创建,缓存无法跨请求共享——应提升到 Singleton 服务中
- 使用
Microsoft.Extensions.Caching.Memory.IMemoryCache时,key 必须可序列化且稳定;建议用MemoryCacheEntryOptions.SlidingExpiration防止无限增长 - EF Core 的
Where(expression)内部已缓存编译结果(针对相同表达式树结构),但仅限于 EF 自己解析的子集;手写复杂表达式仍需自行管理
替代方案:Expression.Compile() 的轻量级选项
如果只是需要“类似委托”的调用能力,且表达式结构固定,可考虑绕过编译:
- 用
Expression.Evaluate()(需引入System.Linq.Expressions.Extensions第三方包)——解释执行,无编译开销,适合低频、调试场景 - 预定义一组常用表达式模板(如
static readonly Expression),运行时替换参数节点再编译,减少重复生成> EqualTo = ... - .NET 7+ 支持
Expression.CompileFast()(非官方 API,来自FastExpressionCompiler库),比原生快 2–5 倍,且支持更广的表达式语法
真正复杂的动态逻辑(如用户自定义规则引擎),建议直接上 Roslyn 编译 C# 字符串为程序集,虽然启动慢,但执行期零开销,且可卸载。









