ASP.NET Core C# 反射 & 表达式树 (第三篇)
前言
前一篇讲完了反射, 这一篇来讲一下和反射息息相关的表达式树.
首先搞清楚 Delegate, Action, Func, Anonymous Method, Lambda, Expression tree
看大神的文章: C#中的Lambda表达式和表达式树
简单说, Delegate 委托是上古年代的东西,
后来有了泛型, 就演化成 Action 和 Func
然后开始玩匿名函数就有了 Lambda 表达式
然后又出来了一个表达式树.
Lambda 其实就是 Delegate, Action, Func 一样的东西.
而表达式树, 则是一些方法调用的表示手法.
表达式树由很多表达式组成, 经过 compile 可以变成 Lambda, 然后拿去执行.
也可以 parse 这个表达式树, 翻译成其它的东西.
比如 EF Core,
var person = await DbContext.People.Where(p => p.Name == "Derrick").ToListAsync();
上面这一句它可以翻译成 SQL
SELECT * FROM People WHERE Name = 'Derrick';
这篇不会讲到如何做翻译, 只会讲到如何动态创建表达式树.
Dynamic Expression How It Work
模拟一下动态创建 Expression for call EF Core's SingleAsync 方法.
public class Person {
public string Name { get; set; } = "";
}
public class Program
{
public static void Main()
{
var people = new List<Person> {
new Person { Name = "n1" },
new Person { Name = "n2" },
new Person { Name = "n3" },
new Person { Name = "n4" },
};
var person = people.AsQueryable().Single(p => p.Name == "n1");
// p => p.Name == "n1"
var pExp = Expression.Parameter(typeof(Person), "p"); // p
var pDotNameExp = Expression.Property(pExp, "Name"); // p.Name
var valueExp = Expression.Constant("n1"); // "n1"
var pDotNameEqualValueExp = Expression.Equal(pDotName, value); // p.Name == "n1"
var lambda = (Expression<Func<Person, bool>>)Expression.Lambda(pDotNameEqualValueExp, new ParameterExpression[] { pExp }); // p => p.Name == "n1"
var lambdaDelegate = lambda.Compile(); // 把表达式树 compile 成可执行的委托
var person2 = people.AsQueryable().Single(lambda); // queryable 情况下参数是表达式, 因为要翻译成 SQL
var person3 = people.Single(lambdaDelegate); // LINQ 的情况下参数是委托, 因为它是直接执行的
}
}
需要动态创建的表达式是这一句 p => p.Name == "n1"
从上面可以看到, 它是逐个逐个通过 Expression 创建和拼接出来的. 看注释一句一句理解.
你会发现它的语法有一种 left right 的感觉, 然后拼着拼着就有点像棵树了, 二叉树

通常, 最后会把 Expression 变成 lambda 作为方法的参数, 需不需要 compile 就看最终拿来干什么. 如果是 Queryable 就直接给表达式树它翻译, 如果是 LINQ 就 compile 成委托执行.
多一个例子
var people = new List<Person> {
new Person { Age = 1 },
new Person { Age = 2 },
new Person { Age = 3 },
new Person { Age = 4 },
};
var person = people.AsQueryable().Where(p => p.Age >= 1 && p.Age <= 3);
// p => p.Age >= 1 && p.Age <= 3
var pExp = Expression.Parameter(typeof(Person), "p"); // p
var pDotAgeExp = Expression.Property(pExp, "Age"); // p.Age
var value1Exp = Expression.Constant(1); // 1
var pDotAgeGreatThanOrEqualvalue1Exp = Expression.GreaterThanOrEqual(pDotAgeExp, value1Exp); // p.Age >= 1
var value3Exp = Expression.Constant(3); // 3
var pDotAgeLessThanOrEqualvalue3Exp = Expression.LessThanOrEqual(pDotAgeExp, value3Exp); // p.Age <= 1
var and = Expression.And(pDotAgeGreatThanOrEqualvalue1Exp, pDotAgeLessThanOrEqualvalue3Exp); // p.Age >= 1 && p.Age <= 3
var lambda = (Expression<Func<Person, bool>>)Expression.Lambda(and, pExp); // p => p.Age >= 1 && p.Age <= 3
var lambdaDelegate = lambda.Compile();
var result = people.Where(lambdaDelegate).ToList(); // [1,2,3]
不管表达式如何复杂, 它就是逐个逐个去拼接, 所以不要害怕. Expression 里面有非常非常都多方法, 几乎你能写出来的 code 都能找到对应的 Expression
执行 lambdaDelegate
上面例子中, 我们都是把 Compile() 后的 lambdaDelegate pass 给 Single 和 Where 方法执行.
这里给一个自己执行的例子.
在第一篇 Anonymous method / Delegate, 我给过三种调用 delegate 的方式.



lambdaDelegate 也是委托, 按理说应该完全适用
第一种方式是类型清楚的, 这个没有问题
// width => width > 100;
var widthParameterExp = Expression.Parameter(typeof(int), "width");
var greaterThan100Exp = Expression.GreaterThan(widthParameterExp, Expression.Constant(100));
var lambdaDelegate = Expression.Lambda<Func<int, bool>>(greaterThan100Exp, widthParameterExp).Compile();
var isGreaterThan100 = lambdaDelegate(150); // True
第二种是类型不清楚的
var lambdaDelegate = Expression.Lambda(greaterThan100Exp, widthParameterExp).Compile();
var isGreaterThan100 = (bool)lambdaDelegate.Method.Invoke(lambdaDelegate.Target, new object[] { 150 })!;
结果报错了 : MethodInfo must be a runtime MethodInfo
第三种 DynamicInvoke 则没有问题
var isGreaterThan100 = (bool)lambdaDelegate.DynamicInvoke(new object[] { 150 })!;
注意: DynamicInvoke 是很慢的, 虽然是搞反射, 但如果能知道部分类型, 比如第一种方式, 那就尽量用第一种吧.
Expression.Call
表达式中也可以表达方法调用哦. 当然这个对翻译 SQL 的话可能会有点问题啦. 但是 for 执行的话就很好用哦.
public class Person {
public int Age { get; set; }
public bool IsDeleted(int value)
{
return true;
}
}
public class Program
{
public static void Main()
{
var people = new List<Person> {
new Person { Age = 1 },
new Person { Age = 2 },
new Person { Age = 3 },
new Person { Age = 4 },
};
// p => p.IsDeleted(5)
var pExp = Expression.Parameter(typeof(Person), "p"); // p
var method = typeof(Person).GetMethod("IsDeleted")!; // 通过反射获取 MethodInfo
var callMethodExp = Expression.Call(pExp, method, new Expression[] { Expression.Constant(5) }); // p.IsDeleted(5)
var lambda = (Expression<Func<Person, bool>>)Expression.Lambda(callMethodExp, pExp); // p => p.IsDeleted(5)
var lambdaDelegate = lambda.Compile();
var result = people.Where(lambdaDelegate).ToList(); // [1,2,3,4]
}
}
总结
动态创建表达式树, 可以帮助我们更好的管理 EF.Core 的代码. OData 也是通过这个方式去写入 EF Core 的 OData -> EF Core -> SQL.
以前我不喜欢写反射, 表达式树, 但后来我发现, 只要把小方法写好, 一点一点拼接上来, 它只是代码多, 但是并不会乱. 所以不要怕.
随便提一嘴,表达式树一直没有更新,许多 C# 新语法都不支持,相关 Github Issue – Extend expression trees to cover more/all language constructs
ASP.NET Core C# 反射 & 表达式树 (第三篇)的更多相关文章
- ASP.NET Core中使用表达式树创建URL
当我们在ASP.NET Core中生成一个action的url会这样写: var url=_urlHelper.Action("Index", "Home"); ...
- 使用Asp.Net Core MVC 开发项目实践[第三篇:基于EF Core的扩展]
上篇我们说到了EFCore的基础使用,这篇我们将讲解下基于EFCore的扩展. 我们在Mango.Framework.EFCore类库项目中创建一个类名EFExtended的扩展类,并且引入相关的命名 ...
- ASP.NET Core Web开发学习笔记-1介绍篇
ASP.NET Core Web开发学习笔记-1介绍篇 给大家说声报歉,从2012年个人情感破裂的那一天,本人的51CTO,CnBlogs,Csdn,QQ,Weboo就再也没有更新过.踏实的生活(曾辞 ...
- 从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件
标题:从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/112 ...
- C#动态构建表达式树(三)——表达式的组合
C#动态构建表达式树(三)--表达式的组合 前言 在筛选数据的过程中,可能会有这样的情况:有一些查询条件是公共的,但是根据具体的传入参数可能需要再额外增加一个条件.对于这种问题一般有两种方法: a. ...
- ASP.NET自定义控件组件开发 第一章 第三篇
原文:ASP.NET自定义控件组件开发 第一章 第三篇 第三篇:第一章的完结篇 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.NET自定义控件组件开发 第一章 第二篇 接着待 ...
- ASP.NET自定义控件组件开发 第一章 第三篇 第一章的完结篇
ASP.NET自定义控件组件开发 第一章 第三篇 第三篇:第一章的完结篇 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.NET自定义控件组件开发 第一章 第二篇 接着待续 ...
- C# 反射 表达式树 模糊搜索
反射实体T,非datetime字段反射获取表达式树 public static Expression<Func<T, bool>> GetSearchExpression& ...
- ASP.NET Core 中的依赖注入 [共7篇]
一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了 ...
- ASP.NET Core 快速入门【第二弹-实战篇】
上篇讲了asp.net core在linux上的环境部署.今天我们将做几个小玩意实战一下.用到的技术和工具有mysql.websocket.AngleSharp(爬虫html解析).nginx多站点部 ...
随机推荐
- Webpack3.x升级至 4.x 小记
近期项目部署遇到点问题,需要升级webpack版本,特此整理一小记,记录升级过程中的依赖包及报错处理. 本次升级的依赖包及对应版本对照表: npm 包 当前版本 升级版本 S/D vue ^2.5.1 ...
- Python 基于Python生成短8位唯一id解决方案
基于Python生成短8位唯一id解决方案 by:授客 QQ:1033553122 测试环境: Win10 Python 3.5.4 实现思路 利用62个可打印字符,通过随机生成32位UUID,由 ...
- ABC361
A link 先输出前\(k\)个,再输出\(x\),最后输出后面的. 点击查看代码 #include<bits/stdc++.h> using namespace std; int n, ...
- DASCTF 2023 & 0X401七月暑期挑战赛【PWN】(FileEditor篇)
DASCTF 2023 & 0X401七月暑期挑战赛[PWN](FileEditor篇) 题目保护情况(保护全家桶) 64位ida逆向 模拟了一个类似vim的功能,有打开文件,打印内容,插入行 ...
- golang 学习笔记1
1.go的gin框架,没有预设目录,具体目录可以在网上参考.
- Linux 破解mysql密码
mysql忘记密码怎么办 [root@master ~]# mysql -uroot -pHuawei123123$ mysql: [Warning] Using a password on the ...
- Jmeter函数助手24-longSum
longSum函数可用于计算两个或多个长值的和.intSum函数参数值的范围在-2147483648到2147483647之间,而longSum函数的参数值范围比intSum的大. First lon ...
- 《最新出炉》系列初窥篇-Python+Playwright自动化测试-61 - 隐藏元素定位与操作
1.简介 对于前端隐藏元素,一直是自动化定位元素的隐形杀手,让人防不胜防.脚本跑到隐藏元素时位置时报各种各样的错误,可是这种隐藏的下拉菜单又没有办法避免,所以非常头痛,这一篇只为交流隐藏元素自动化定位 ...
- 基于 ChatGPT 的聊天软件合集打包分享
「基于 ChatGPT 的聊天软件合集打包」 链接:https://pan.quark.cn/s/ef1f5e9c48e4 BotGem(原名AMA) 官网:https://botgem.com/ ...
- Google搜索居然也搞言论封锁
昨天的一个blog: https://www.cnblogs.com/devilmaycry812839668/p/18334275 居然被Google搜索封禁了: 老美,你的言论自由呢???怎么到了 ...