前言

前一篇讲完了反射, 这一篇来讲一下和反射息息相关的表达式树.

首先搞清楚 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# 反射 & 表达式树 (第三篇)的更多相关文章

  1. ASP.NET Core中使用表达式树创建URL

    当我们在ASP.NET Core中生成一个action的url会这样写: var url=_urlHelper.Action("Index", "Home"); ...

  2. 使用Asp.Net Core MVC 开发项目实践[第三篇:基于EF Core的扩展]

    上篇我们说到了EFCore的基础使用,这篇我们将讲解下基于EFCore的扩展. 我们在Mango.Framework.EFCore类库项目中创建一个类名EFExtended的扩展类,并且引入相关的命名 ...

  3. ASP.NET Core Web开发学习笔记-1介绍篇

    ASP.NET Core Web开发学习笔记-1介绍篇 给大家说声报歉,从2012年个人情感破裂的那一天,本人的51CTO,CnBlogs,Csdn,QQ,Weboo就再也没有更新过.踏实的生活(曾辞 ...

  4. 从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件

    标题:从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/112 ...

  5. C#动态构建表达式树(三)——表达式的组合

    C#动态构建表达式树(三)--表达式的组合 前言 在筛选数据的过程中,可能会有这样的情况:有一些查询条件是公共的,但是根据具体的传入参数可能需要再额外增加一个条件.对于这种问题一般有两种方法: a. ...

  6. ASP.NET自定义控件组件开发 第一章 第三篇

    原文:ASP.NET自定义控件组件开发 第一章 第三篇 第三篇:第一章的完结篇 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.NET自定义控件组件开发 第一章 第二篇 接着待 ...

  7. ASP.NET自定义控件组件开发 第一章 第三篇 第一章的完结篇

    ASP.NET自定义控件组件开发 第一章 第三篇   第三篇:第一章的完结篇 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.NET自定义控件组件开发 第一章 第二篇 接着待续 ...

  8. C# 反射 表达式树 模糊搜索

    反射实体T,非datetime字段反射获取表达式树   public static Expression<Func<T, bool>> GetSearchExpression& ...

  9. ASP.NET Core 中的依赖注入 [共7篇]

    一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了 ...

  10. ASP.NET Core 快速入门【第二弹-实战篇】

    上篇讲了asp.net core在linux上的环境部署.今天我们将做几个小玩意实战一下.用到的技术和工具有mysql.websocket.AngleSharp(爬虫html解析).nginx多站点部 ...

随机推荐

  1. Windows在待机后重新进入桌面出现资源管理器无响应的解决方案

    问题 在日常使用Windows操作系统的过程中,我们可能会遇到一种较为特殊的情况--在系统待机后重新激活桌面时,资源管理器出现无响应现象.这一问题不仅影响用户体验,还可能导致剪切板功能异常,进而影响到 ...

  2. 使用FModel提取游戏资产

    目录 前言 FModel简介 FModel安装 FModel使用 初次使用 资产预览 资产导出 附录 dumper Dumper-7生成usmap文件 向游戏中注入dll 前言 这篇文章仅记录我作为初 ...

  3. 奇思妙想,动手 DIY 你的浏览器主页

    实战开发和上线一个极客范儿的浏览器主页,原来如此简单! 大家好我是鱼皮,前段时间上线了一个程序员必备的浏览器主页,得到了很多同学的好评. 地址:https://home.code-nav.cn/ 其实 ...

  4. iOS开发基础109-网络安全

    在iOS开发中,保障应用的网络安全是一个非常重要的环节.以下是一些常见的网络安全措施及对应的示例代码: Swift版 1. 使用HTTPS 确保所有的网络请求使用HTTPS协议,以加密数据传输,防止中 ...

  5. Day 10 - 动态规划与树状数组

    动态规划基础 主要介绍动态规划的基本思想,以及动态规划中状态及状态转移方程的设计思路,帮助各位初学者对动态规划有一个初步的了解. 引入 [IOI1994] 数字三角形. 给定一个 \(r\) 行的数字 ...

  6. 一种优秀的虚拟机内存架构 - AQ

    源链接:https://www.axa6.com/zh/an-excellent-virtual-machine-memory-architecture 简介 虚拟机内存架构直接影响虚拟机的性能和占用 ...

  7. 使用win server 2019服务器的iis服务发布静态网页

    1.首先远程连接到服务器 2.打开服务器管理器 3添加角色和功能 4.安装类型:选择基于角色或基于功能的安装  →服务器角色:从服务器池中选择服务器 5.服务器角色选择Web服务器(iis) 6.功能 ...

  8. Java 根据XPATH批量替换XML节点中的值

    根据XPATH批量替换XML节点中的值 by: 授客 QQ:1033553122 测试环境 JDK 1.8.0_25 代码实操 message.xml文件 <Request service=&q ...

  9. [UE源码] 关于使用UE待改进的一些尝试

    UE从自己做了一款游戏后,发现了蓝图以及UE引擎本身的一些优缺点: 1.蓝图在一些简单的逻辑上书写方便,直观,而且编译速度快,但是也有一些其他问题: 结构体赋值后,无法二次修改 只有3种容器Array ...

  10. java面试一日一题:讲下mysql中的锁

    问题:请讲下在mysql中的锁 分析:该问题主要考察对中锁的掌握,主要考察的是读.写锁.行锁.间隙锁.next-key,其他还有表锁.意向锁 回答要点: 主要从以下几点去考虑, 1.mysql中的锁有 ...