当前位置: 首页 > news >正文

C# Web开发教程(四)

EF Core SQL性能优化底层原理之表达式树(Expression Trees)

好的,我们来详细探讨一下表达式树(Expression Trees)在 Entity Framework Core 中的核心作用。

简单来说,表达式树是 EF Core 能够将你的 C# 代码(如 LINQ 查询)翻译成高效 SQL 语句的基石。没有表达式树,EF Core 就无法实现其最核心、最有价值的功能。


核心作用:提供可翻译(Translatable)的查询结构

表达式树的核心作用在于,它不是一个可执行的方法,而是一个数据结构。这个数据结构可以被 EF Core 的查询提供程序(Query Provider,例如 SqlServerQueryProvider) 分析、解读,并最终转换为目标数据库的 SQL 语言。

1. 与委托(Delegate)的关键区别

要理解表达式树,最好先把它和普通的委托(如 Func<T, bool>)对比一下。

  • 使用 Func<T, bool>(委托,即内存中的方法):

    Func<Post, bool> myDelegate = p => p.Title.Contains("EF Core");
    

    这是一个可立即执行的代码。如果你在 DbSet<Post> 上使用 .Where(myDelegate),EF Core 无法看到 p.Title.Contains("EF Core") 这个逻辑。它只能调用这个方法,并得到返回的 truefalse。结果就是,EF Core 必须从数据库拉取所有 Post 数据到内存中,然后在客户端进行过滤。这在大多数情况下性能极差。

  • 使用 Expression<Func<T, bool>>(表达式树):

    Expression<Func<Post, bool>> myExpression = p => p.Title.Contains("EF Core");
    

    这是一个数据的描述。它不是一个可执行的方法,而是一个可以被遍历和检查的树形结构。EF Core 可以解析这棵树,发现它由以下节点组成:

    • 一个参数 p(类型为 Post
    • 访问 pTitle 属性
    • 调用 String.Contains 方法,参数是 "EF Core"
    • 等等...

    正因为 EF Core 能读懂你的意图,它才能生成对应的 SQL 语句:

    SELECT [p].[Id], [p].[Title], [p].[Content], ...
    FROM [Posts] AS [p]
    WHERE [p].[Title] LIKE N'%EF Core%'
    

    查询在数据库服务器上执行,只返回匹配的结果,效率极高。

2. 实现跨数据库平台的查询翻译

EF Core 支持多种数据库(SQL Server, SQLite, PostgreSQL, MySQL等)。其底层为每个数据库提供了不同的查询提供程序。

  • 表达式树提供了一个公共的、抽象的查询中间语言
  • 当你编写 LINQ 查询时,EF Core 会将其构建为表达式树。
  • 然后,SQL Server 提供程序会将其翻译成 T-SQL,而 SQLite 提供程序则会将其翻译成 SQLite 的 SQL 方言。

这个过程使得你可以用同一套 C# LINQ 代码与不同的数据库进行交互。

3. 实现高效的延迟执行(Deferred Execution)

著名的 IQueryable<T> 接口(提供 LINQ 查询功能的接口)的核心就是一个表达式树和一个查询提供程序。

IQueryable<Post> query = _context.Posts.Where(p => p.Likes > 10).OrderBy(p => p.CreatedDate);
  • 这行代码并没有立即执行查询。
  • 它只是构建了一个表达式树,将 .Where.OrderBy 操作依次组合起来。
  • 只有当你真正需要数据时(例如调用 .ToList().FirstOrDefault()foreach 循环),EF Core 才会将整个表达式树翻译成 SQL 并执行。

这种“组合性”是构建复杂动态查询的基础。

4. 支持动态查询构建

这是表达式树非常强大的一个高级用法。因为表达式树是可以在运行时动态构建和修改的,所以你可以在程序运行时根据用户输入或其他条件来动态创建查询。

示例:根据用户选择动态过滤

// 假设这是用户从前端传递过来的过滤条件
string searchTitle = "Hello";
DateTime? minDate = new DateTime(2023, 1, 1);// 从基础查询开始
IQueryable<Post> query = _context.Posts;// 动态添加 Where 条件
if (!string.IsNullOrEmpty(searchTitle))
{// 动态构建表达式: p => p.Title.Contains(searchTitle)query = query.Where(p => p.Title.Contains(searchTitle));
}if (minDate.HasValue)
{// 动态构建表达式: p => p.CreatedDate >= minDatequery = query.Where(p => p.CreatedDate >= minDate.Value);
}// 最终执行的 SQL 会组合所有条件
var results = query.ToList();

EF Core 会将所有动态添加的条件组合到最终的表达式树中,并生成一个包含所有过滤条件的单一、高效的 SQL 语句。


总结

表达式树在 EF Core 中的作用可以概括为以下几点:

作用 说明
查询翻译 最核心的作用。将 C# LINQ 代码转换为等价的 SQL 语句,确保查询在数据库端执行,而不是在客户端内存中。
提供抽象层 作为公共中间语言,使同一套 LINQ 代码可以跨不同数据库工作。
实现延迟执行 使得 IQueryable 可以组合多次查询操作,最后再统一翻译和执行。
支持动态查询 允许在运行时程序化地构建复杂的查询逻辑,极大提升了灵活性。

简而言之,表达式树是 EF Core 的“翻译官”,它让 C# 语言能够与 SQL 数据库进行高效、无缝的对话。 没有表达式树,LINQ to SQL 之类的 ORM 技术就无法实现其核心价值。

表达式树测试

  • 安装第三方库 Install-Package ExpressionTreeToString实现打印表达式树结构
......
using ExpressionTreeToString;
......
Expression<Func<Article, bool>> res = a => a.Price > 0; // 创建表达式树
Console.WriteLine(res.ToString("Object notation","C#")); // 使用 ExpressionTreeToString 输出表达式树- 结果:var a = new ParameterExpression {Type = typeof(Article),IsByRef = false,Name = "a"
};new Expression<Func<Article, bool>> {NodeType = ExpressionType.Lambda,Type = typeof(Func<Article, bool>),Parameters = new ReadOnlyCollection<ParameterExpression> {a},Body = new BinaryExpression {NodeType = ExpressionType.GreaterThan,Type = typeof(bool),Left = new MemberExpression {Type = typeof(int),Expression = a,Member = typeof(Article).GetProperty("Price")},Right = new ConstantExpression {Type = typeof(int),Value = 0}},ReturnType = typeof(bool)
}

总结

它:

  1. 展示了表达式树的内部结构层次
  2. 揭示了每个表达式节点的各种属性(如 NodeType, Type 等)
  3. 说明了表达式树是如何由多个嵌套表达式组成的
  4. 提供了如何手动构建相同表达式树的"配方"

虽然这个输出不能直接编译执行,但它对于理解表达式树的内部工作原理非常有帮助,特别是在调试复杂表达式或学习表达式树API时

http://www.rkmt.cn/news/2167.html

相关文章:

  • HarmonyOS运动开发
  • 【2025-09-09】家庭决策
  • 多变量递归-全排列问题
  • Gitee DevOps:中国开发者效率革命的本土化解决方案
  • fastapi
  • Oracle主键primary key
  • Kubernetes标签(Label)
  • MCU联网
  • 算法-A*-01 - jack
  • [antlr] 如何在Linux(Ubuntu)环境中安装配置antlr4.9.2
  • 国内开发者如何选择代码管理平台?Gitee、GitHub与Bitbucket深度对比
  • Spring-Android-即时入门-全-
  • 4. 链表
  • 测试用例设计检查项
  • 版本发布| IvorySQL 4.6 发布
  • Avalonia Calendar 日历控件遇到 Flyout 或者切换页面时出现的鼠标按下失效的解决方法
  • Vue 2 + Element UI 技术栈的管理端项目和Git使用教程
  • ubuntu22.04.5系统重启后网络配置消失问题
  • 第十届计算机技术与机械电气工程国际学术论坛(ISCME 2025)暨2025年泰山学术论坛-鲁东大学微纳传感器及系统专题论坛
  • FinRL(2)China_A_share_market_tushare.ipynb
  • 应急响应:某网站被挂非法链接
  • 用惯了VO,什么时候需要DTO?
  • WPF 警惕 StylusPlugIn 的多线程安全问题
  • RAG or 微调
  • 什么是AI CRM(人工智能客户关系管理)
  • 完整教程:WPF WriteableBitmap 高性能双缓冲图片显示方案
  • cache的基本原理
  • 如何用 vxe-table 实现2个树表格可以互相拖拽数据
  • CSP 初赛必背
  • 最新安卓版16音轨简谱编辑器软件使用说明