網(wǎng)站首頁 編程語言 正文
什么是表達(dá)式樹
表達(dá)式樹以樹形數(shù)據(jù)結(jié)構(gòu)表示代碼,其中每一個(gè)節(jié)點(diǎn)都是一種表達(dá)式,比如方法調(diào)用和 x < y 這樣的二元運(yùn)算等。可以對表達(dá)式樹中的代碼進(jìn)行編輯和運(yùn)算。 這樣能夠動態(tài)修改可執(zhí)行代碼、在不同數(shù)據(jù)庫中執(zhí)行 LINQ 查詢以及創(chuàng)建動態(tài)查詢。 表達(dá)式樹還能用于動態(tài)語言運(yùn)行時(shí) (DLR) 以提供動態(tài)語言和 .NET 之間的互操作性,同時(shí)保證編譯器編寫員能夠發(fā)射表達(dá)式樹而非 Microsoft 中間語言 (MSIL)。 這段話是來自官網(wǎng)( [表達(dá)式樹 (C#) | Microsoft Docs](表達(dá)式樹 (C#) | Microsoft Docs) )的定義。
在 C# 中,我們可以通過 Expression 的方式來手動創(chuàng)建表達(dá)式樹,比如:
[HttpGet] public IActionResult Expression() { // 查詢 年齡Age 大于 18 的元素 Expression<Func<User,bool>> expression1 = x => x.Age > 18; return Ok(); }
那么,x.Age > 18 這一表達(dá)式,它的樹狀結(jié)構(gòu)是這樣的:
通過 Visual Studio 自帶的查看變量或添加監(jiān)視的方式,我們可以發(fā)現(xiàn)其中 樹的根節(jié)點(diǎn)(NodeType)是 GreaterThan,左節(jié)點(diǎn)(Left)是 x.Age,右節(jié)點(diǎn)(Right)是 18。所以由此就可以大概畫出樹狀結(jié)構(gòu)。
最后,通過這種樹狀結(jié)構(gòu),C# 就可以幫我們將表達(dá)式編譯成具體的 SQL 執(zhí)行語句。
如果想更清晰的查看表達(dá)式樹的結(jié)構(gòu),可以 nuget 一個(gè)包( ExpressionTreeToString ),將表達(dá)式結(jié)構(gòu)轉(zhuǎn)換成字符串
PM> Install-Package ZSpitz.Util -Version 0.1.116
Expression<Func<User, bool>> expression = u => u.Age >= 18; var treeStr = expression.ToString("Object notation", "C#"); // 輸出為下面字符串 var u = new ParameterExpression { Type = typeof(User), IsByRef = false, Name = "u" }; new Expression<Func<User, bool>> { NodeType = ExpressionType.Lambda, Type = typeof(Func<User, bool>), Parameters = new ReadOnlyCollection<ParameterExpression> { u }, Body = new BinaryExpression { NodeType = ExpressionType.GreaterThanOrEqual, Type = typeof(bool), Left = new MemberExpression { Type = typeof(int), Expression = u, Member = typeof(User).GetProperty("Age") }, Right = new ConstantExpression { Type = typeof(int), Value = 18 } }, ReturnType = typeof(bool) }
Expression 和 Func 的區(qū)別
- Expression 存儲了運(yùn)算邏輯,可以將其保存成抽象語法樹(AST),可以在運(yùn)行時(shí)動態(tài)獲取運(yùn)算邏輯。
- Func 只是存儲了結(jié)果,無法保存成語法樹,也無法動態(tài)獲取運(yùn)算邏輯。
所以,在 EFCore 中,使用表達(dá)式對數(shù)據(jù)庫數(shù)據(jù)進(jìn)行查詢中,我們應(yīng)該選擇 Expression 而不是 Func,因?yàn)槭褂昧?Func ,實(shí)際上并無法將 Func 中的表達(dá)式轉(zhuǎn)換成 SQL,而是在將所有數(shù)據(jù)加載到內(nèi)存后,在內(nèi)存中在過濾 Func 中的條件。
簡單來說就是,此時(shí)要篩選 User 表中年齡大于18的數(shù)據(jù),可以有這兩種寫法
// 這種寫法,實(shí)際生成的 SQL 語句, 大概是這樣的 SELECT * FROM User as T WHERE T.age > 18 Expression<Func<User,bool>> expression1 = x => x.Age > 18; dbContext.User.Where(expression1).toList(); // 而這種, 生成的語句是這樣的 SELECT * FROM User, 然后將 User 表中所有數(shù)據(jù)加載到內(nèi)存中后, 在進(jìn)行 age > 18 的過濾 Func<User, bool> func1 = x => x.Age > 18; dbContext.User.Where(func1).toList();
通過代碼創(chuàng)建表達(dá)式樹
- ParameterExpression
- BinaryExpression
- MethodCallExpression
- ConstantExpression
這些類幾乎都沒有提供構(gòu)造方法,而且所有的屬性都幾乎只是只讀。因此我們一般不會直接創(chuàng)建這些類的實(shí)例,而是調(diào)用 Expression 類的 Parameter、MakeBinary、Call、Constant等靜態(tài)方法來生成,這些靜態(tài)方法我們一般稱作創(chuàng)建表達(dá)式樹的工廠方法,而屬性則通過方法參數(shù)類設(shè)置。
動態(tài)將表達(dá)式:u => u.Age >= 18; 通過代碼構(gòu)建出來
一般構(gòu)建步驟:
- 先創(chuàng)建 ParameterExpression
- 接著由里到外逐步構(gòu)建
- 先左節(jié)點(diǎn)(Left)
- 后右節(jié)點(diǎn)(Right)
- 接著Body節(jié)點(diǎn)
- 將其拼接成 Expression
public IActionResult GetUserByManualExpression() { ParameterExpression parameterExpression = Expression.Parameter(type:typeof(User), name: "u"); ConstantExpression right = Expression.Constant(18); MemberExpression left = Expression.MakeMemberAccess(parameterExpression, member: typeof(User).GetProperty("Age")); BinaryExpression body = Expression.GreaterThanOrEqual(left, right); Expression<Func<User, bool>> expression = Expression.Lambda<Func<User, bool>>(body, parameters: parameterExpression); var data = _userService.GetUsers(expression); return Ok(new { code = 200, msg = "OK", data }); }
原文鏈接:https://www.cnblogs.com/chenyanbo1024/p/15723922.html
相關(guān)推薦
- 2022-10-14 nginx 反向代理以及 location /admin/
- 2022-01-17 git git版本回退 回滾 解決方案
- 2022-11-07 React?全面解析excel文件_React
- 2023-03-22 Nginx轉(zhuǎn)發(fā)丟失cookie表現(xiàn)形式及解決方案_nginx
- 2022-06-14 利用Python?NumPy庫及Matplotlib庫繪制數(shù)學(xué)函數(shù)圖像_python
- 2022-07-20 centos 安裝jenkins 實(shí)現(xiàn)自動部署到遠(yuǎn)程服務(wù)器 (樹莓派可用)
- 2022-12-26 React?tabIndex使非表單元素支持focus和blur事件_React
- 2022-10-14 SpringCloud 微服務(wù)與遠(yuǎn)程調(diào)用測試
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支