EF Core表達式樹

文章目錄

  • 前言
  • 一、表達式樹與委托的區別
  • 二、動態構建表達式樹
    • 示例1
    • 示例2
    • 示例3
    • 高級技巧:表達式合并
  • 三、ExpressionTreeToString
    • 安裝方法
    • 基本用法
    • 支持的格式化風格
  • 四、注意事項
  • 總結


前言

在 Entity Framework Core 中,表達式樹(Expression Tree) 是 LINQ 查詢的核心機制,它允許將 C# 代碼中的查詢邏輯轉換為 SQL 語句,從而在數據庫服務器端高效執行。

一、表達式樹與委托的區別

  1. 委托(如 Func<T, bool>
    直接編譯為可執行的代碼,運行時在內存中過濾數據(客戶端評估)。

    Func<House, bool> func = p => p.Owner.Contains("tom");
    var res=dbContext.Houses.Where(exp1).ToList(); // 在客戶端過濾!
    
  2. 表達式樹(如 Expression<Func<T, bool>>
    保持查詢邏輯的抽象語法樹結構,EF Core 可將其轉換為 SQL服務器端評估)。

    Expression<Func<House, bool>> exp1 = b => b.Owner.Contains("tom");
    var res=dbContext.Houses.Where(exp1).ToList();//生成 SQL:WHERE Owner like '%tom%'
    

二、動態構建表達式樹

  1. 當需要根據運行時條件動態生成查詢時,手動構建表達式樹非常有用。
  2. ParameterExpressionBinaryExpressionMethodCallExpressionConstantExpression等類幾乎都沒有提供構造方法,而且所有屬性也幾乎都是只讀,因此我們一般不會直接創建這些類的實例,而是調用Expression類的ParameterMakeBinaryCallConstant等靜態方法來生成,這些靜態方法我們一般稱作創建表達式樹的工廠方法,而屬性則是通過方法參數類設置。
  3. 工廠方法
    加法:Add
    短路與運算:AndAlso
    數組元素訪問:ArraryAccess
    方法訪問:Call
    三元條件運算符:Condition
    常量表達式:Constant
    類型轉換:Convert
    大于運算符:GreaterThan
    小于運算:LessThan
    大于或等于運算符:GreaterThanOrEqual
    創建二元運算:MakeBinary
    不等于運算:NotEqual
    短路或運算:OrElse
    表達式的參數:Parameter

示例1

  1. 動態過濾Owner包含關鍵字

    using System.Linq.Expressions;
    using (MyDBContext dbContext=new MyDBContext())
    {string name = Console.ReadLine();// 參數表達式:代表實體對象(如 p => ... 中的 p)ParameterExpression param = Expression.Parameter(typeof(House), "p");// 屬性訪問:p.OwnerMemberExpression nameProperty = Expression.Property(param, "Owner");MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });// 參數值ConstantExpression keywordConstant = Expression.Constant(name);MethodCallExpression nameCondition = Expression.Call(nameProperty, containsMethod, keywordConstant);// 組合為 Lambda 表達式Expression<Func<House, bool>> expr =Expression.Lambda<Func<House, bool>>(nameCondition, param);// 應用查詢var query = dbContext.Houses.Where(expr);foreach (var house in query){Console.WriteLine(house.Owner);}
    }
    
  2. 生成的 SQL

    SELECT * FROM Houses WHERE Owner like '%tom%'
    

示例2

  1. 動態過濾價格

    using System.Linq.Expressions;// 參數表達式:代表實體對象(如 p => ... 中的 p)
    ParameterExpression param = Expression.Parameter(typeof(House), "p");// 屬性訪問:p.Price
    MemberExpression priceProperty = Expression.Property(param, "Price");// 常量值:100
    ConstantExpression constant = Expression.Constant(100.0);// 比較表達式:p.Price > 100
    BinaryExpression priceComparison = Expression.GreaterThan(priceProperty, constant);// 組合為 Lambda 表達式
    Expression<Func<House, bool>> expr = Expression.Lambda<Func<House, bool>>(priceComparison, param);// 應用查詢
    var query = dbContext.Houses.Where(expr);
    
  2. 生成的SQL

    SELECT * FROM T_Houses WHERE Price > 100
    

示例3

  1. 組合多個表達式(動態查詢中,常需要組合多個條件(如 AND/OR))

    public static Expression<Func<House, bool>> BuildDynamicFilter(string paramOwnerStr,string paramPriceStr,
    string nameKeyword, double? minPrice)
    {ParameterExpression param = Expression.Parameter(typeof(House), "p");Expression finalExpr = Expression.Constant(true); // 初始條件:true// 條件1:名稱包含關鍵字if (!string.IsNullOrEmpty(nameKeyword)){MemberExpression nameProperty = Expression.Property(param, paramOwnerStr);MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });ConstantExpression keywordConstant = Expression.Constant(nameKeyword);MethodCallExpression nameCondition = Expression.Call(nameProperty, containsMethod, keywordConstant);finalExpr = Expression.AndAlso(finalExpr, nameCondition);}// 條件2:價格 >= minPriceif (minPrice.HasValue){MemberExpression priceProperty = Expression.Property(param, paramPriceStr);ConstantExpression minPriceConstant = Expression.Constant(minPrice.Value);BinaryExpression priceCondition = Expression.GreaterThanOrEqual(priceProperty, minPriceConstant);finalExpr = Expression.AndAlso(finalExpr, priceCondition);}return Expression.Lambda<Func<House, bool>>(finalExpr, param);
    }
    
     // 使用:using (MyDBContext dbContext =new MyDBContext()){var filter = BuildDynamicFilter("Owner","Price","Tom", 2000.0);var query = dbContext.Houses.Where(filter);foreach (var house in query){Console.WriteLine(house.Owner);}}
    
  2. 生成的SQL

     SELECT [t].[Id], [t].[Name], [t].[Owner], [t].[Price], [t].[RowVersion]FROM [T_Houses] AS [t]WHERE [t].[Owner] LIKE N'%Tom%' AND [t].[Price] >= 2000.0E0
    

高級技巧:表達式合并

  1. 如果需要組合兩個已有的表達式(如 expr1 && expr2),需統一參數。

  2. 示例:合并兩個表達式

     public static Expression<Func<T, bool>> CombineAnd<T>(Expression<Func<T, bool>> expr1,Expression<Func<T, bool>> expr2){var param = Expression.Parameter(typeof(T));var body1 = ReplaceParameter(expr1.Body, expr1.Parameters[0], param);var body2 = ReplaceParameter(expr2.Body, expr2.Parameters[0], param);return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(body1, body2),param);}private static Expression ReplaceParameter(Expression expression,ParameterExpression oldParam,ParameterExpression newParam){return new ParameterReplacer(oldParam, newParam).Visit(expression);}class ParameterReplacer : ExpressionVisitor{private readonly ParameterExpression _oldParam;private readonly ParameterExpression _newParam;public ParameterReplacer(ParameterExpression oldParam, ParameterExpression newParam){_oldParam = oldParam;_newParam = newParam;}protected override Expression VisitParameter(ParameterExpression node){return node == _oldParam ? _newParam : node;}}
    
     使用:
    Expression<Func<House, bool>> expr1 = p => p.Owner.Contains("Tom");
    Expression<Func<House, bool>> expr2 = p => p.Price > 2000;
    var combinedExpr = CombineAnd(expr1, expr2);
    var query2 = dbContext.Houses.Where(combinedExpr);
    foreach (var house in query2)
    {Console.WriteLine(house.Owner);
    }
    
  3. 生成的SQL

    SELECT [t].[Id], [t].[Name], [t].[Owner], [t].[Price], [t].[RowVersion]FROM [T_Houses] AS [t]WHERE [t].[Owner] LIKE N'%Tom%' AND [t].[Price] > 2000.0E0
    

三、ExpressionTreeToString

ExpressionTreeToString 是一個第三方庫,用于將 LINQ 表達式樹(Expression)轉換為可讀的字符串形式,幫助開發者調試和分析表達式樹的結構。
輸出的所有代碼都是對于工廠方法的調用,且調用工廠方法的時候都省略了Expression類,手動添加Expression或者using static System.Linq.Expressions.Expression;

安裝方法

  1. 通過 NuGet 包管理器安裝

    Install-Package ExpressionTreeToString
    

基本用法

  1. 示例

    Expression<Func<House, bool>> exp1 = b => b.Owner.Contains("tom");
    Expression<Func<House, bool>> exp2 = b => b.Price > 2000;
    //轉換為字符串(支持多種格式化選項)
    //string exprString = expr.ToString("C#", "Dynamic LINQ");//Console.WriteLine(exp1.ToString("Factory methods", "C#"));
    Console.WriteLine(exp2.ToString("Factory methods", "C#"));
    
    //輸出結果展示
    // using static System.Linq.Expressions.Expressionvar b = Parameter(typeof(House),"b"
    );Lambda(GreaterThan(MakeMemberAccess(b,typeof(House).GetProperty("Price")),Constant(2000)),b
    )
    

支持的格式化風格

ExpressionTreeToString 提供多種輸出格式,方便不同場景使用:

  1. C# 語法風格:ToString(“C#”)
    接近 C# 代碼的直觀表示。
  2. Visual Basic 語法風格:ToString(“VB”)
    類似 VB 語法。
  3. 表達式樹結構:ToString(“Object notation”)
    顯示表達式樹的節點結構(如 BinaryExpressionParameterExpression)。
  4. 調試視圖:ToString(“DebugView”)
    Visual Studio 調試器中表達式樹的顯示一致。

四、注意事項

  1. 不支持所有 C# 方法:某些方法(如 ToString())無法轉換為 SQL,會導致運行時錯誤。
  2. 調試技巧:通過 query.ToQueryString() 查看生成的 SQL
  3. 性能:表達式樹構建在內存中完成,復雜邏輯可能影響啟動性能。

總結

通過靈活使用表達式樹,可以極大增強 EF Core 查詢的靈活性,同時保持高效的服務器端執行。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/73999.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/73999.shtml
英文地址,請注明出處:http://en.pswp.cn/web/73999.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

NVM安裝速通使用手冊(Windows版)NVM管理node版本命令手冊 NVM使用手冊

nvm&#xff08;Node Version Manager&#xff09;是一個用于管理Node.js版本的命令行工具。通過nvm&#xff0c;你可以在同一臺機器上安裝和切換多個Node.js版本&#xff0c;非常適合開發和測試在不同Node.js版本上運行的應用程序 一、安裝地址 1. 官方下載&#xff1a; &…

vLLM命令行使用方法詳解

vLLM 是一個針對大語言模型(LLMs)優化的高效推理和服務庫。以下是 vLLM 命令行工具的詳細使用方法解析,涵蓋常見場景和參數配置: 一、核心命令行工具 vLLM 提供兩個主要的命令行入口: 啟動 API 服務器 用于部署 HTTP/OpenAI 兼容的 API 服務: python -m vllm.entrypoint…

# 基于 OpenCV 的選擇題自動批改系統實現

在教育領域&#xff0c;選擇題的批改工作通常較為繁瑣且重復性高。為了提高批改效率&#xff0c;我們可以利用計算機視覺技術&#xff0c;通過 OpenCV 實現選擇題的自動批改。本文將詳細介紹如何使用 Python 和 OpenCV 實現一個簡單的選擇題自動批改系統。 1. 項目背景 選擇題…

python黑科技:無痛修改第三方庫源碼

需求不符合 很多時候&#xff0c;我們下載的 第三方庫 是不會有需求不滿足的情況&#xff0c;但也有極少的情況&#xff0c;第三方庫 沒有兼顧到需求&#xff0c;導致開發者無法實現相關功能。 如何通過一些操作將 第三方庫 源碼進行修改&#xff0c;是我們將要遇到的一個難點…

第十三章:優化內存管理_《C++性能優化指南》_notes

優化內存管理 一、內存管理基礎概念二、自定義分配器三、智能指針優化重點知識代碼示例&#xff1a;智能指針性能對比 四、性能優化關鍵點總結多選題設計題答案與詳解多選題答案設計題示例答案&#xff08;第1題&#xff09; 一、內存管理基礎概念 重點知識 動態內存分配開銷…

python筆記之函數

函數初探 python在要寫出函數很簡單&#xff0c;通過關鍵字def即可寫出&#xff0c;簡單示例如下 def add(a, b):return ab 以上即可以定義出一個簡單的函數&#xff1a;接收兩個變量a和b&#xff0c;返回a和b相加的結果&#xff0c;當然這么說也不全對&#xff0c;原因就是…

【服務器操作指南 - GPU 使用與文件傳輸】輕松掌握 GPU 狀態查看和服務器文件傳輸技巧

0. 引言 在使用服務器時&#xff0c;高效管理 GPU 和文件傳輸是兩項不可或缺的技能。 本指南旨在幫助您快速掌握服務器環境下的 GPU 使用狀態監測方法&#xff0c;并簡要介紹如何在服務器之間進行文件傳輸操作。 1. 查看服務器上的 gpu 使用狀態 1.1 安裝 gpustat 這條指令…

0330-YYYY-MM-DD格式日期比較大小

最簡單的&#xff08;python&#xff09; from datetime import datetime def compare_time(time1,time2): time1_t datetime.strptime(time1,“%Y-%m-%d”) time2_t datetime.strptime(time2,“%Y-%m-%d”) if time1_t < time2_t: return time1_t elif time1_t > ti…

QFlightInstruments飛行儀表控件庫

QFlightInstruments 是一個開源的飛行儀表控件庫&#xff0c;專為基于 Qt 的應用程序設計。它提供了一系列仿真實飛機儀表的組件&#xff0c;適用于飛行模擬軟件、航空電子系統或任何需要高仿真飛行儀表顯示的項目。 主要功能 高仿真飛行儀表&#xff1a;包括空速表、高度表、…

VSCode 市場發現惡意擴展正在傳播勒索軟件!

在VSCode 市場中發現了兩個隱藏著勒索軟件的惡意擴展。其中一個于去年 10 月出現在微軟商店&#xff0c;但很長時間沒有引起注意。 這些是擴展ahban.shiba 和 ahban.cychelloworld&#xff0c;目前已從商店中刪除。 此外&#xff0c;ahban.cychelloworld 擴展于 2024 年 10 月…

國信華源攜AI+水利創新成果亮相第十五屆防汛抗旱信息化技術交流會

直擊展會現場 近日&#xff0c;以“人工智能賦能防汛抗旱 融合創新共御極端災害”為主題的第十五屆防汛抗旱信息化技術交流會在河南鄭州召開。作為水旱災害防御領域的專精企業&#xff0c;北京國信華源科技有限公司攜自主研發的入戶叫應預警系統及覆蓋防汛抗旱全鏈條的智慧化場…

MATLAB語言的鏈表反轉

MATLAB語言的鏈表反轉 鏈表是一種常見的數據結構&#xff0c;與數組相比&#xff0c;鏈表在插入和刪除操作方面具有更高的靈活性。然而&#xff0c;鏈表的一些操作&#xff0c;比如反轉鏈表&#xff0c;對一些初學者來說可能是一個挑戰。本篇文章將重點討論如何使用MATLAB語言…

Oracle數據庫數據編程SQL<2.2 DDL 視圖、序列>

目錄 一、Oracle 視圖(Views) &#xff08;一&#xff09; Oracle 視圖特點 &#xff08;二&#xff09;Oracle 視圖創建語法 關鍵參數&#xff1a; &#xff08;三&#xff09;Oracle 視圖類型 1、普通視圖 2、連接視圖&#xff08;可更新&#xff09; 3、對象視圖 4…

QtAdvancedStylesheets使用

QtAdvancedStylesheets 是一個基于 Qt Widgets 的樣式表(QSS)增強庫,允許開發者通過類似 CSS 的方式深度定制 Qt 應用程序的界面風格,支持動態主題切換、動畫效果和復雜控件樣式設計。 1. 核心功能 高級樣式表支持 使用 CSS-like 語法美化 Qt Widgets(如 QPushButton、Q…

QtAV入門

QtAV 是一個基于 FFmpeg 和 Qt 的高性能多媒體播放框架,提供強大的音視頻解碼、渲染和處理能力,適合開發跨平臺的播放器、視頻編輯和流媒體應用。 1. 核心功能 多格式支持 支持 H.264/H.265、VP9、AV1 等視頻編碼。 支持 MP3、AAC、Opus 等音頻編碼。 封裝格式:MP4、MKV、…

[ C++ ] | C++11 從左值引用到右值引用

&#xff08;目錄占位&#xff09; 1. 前言&#xff1a; C 11 是在 C 98 之后又一個變化比較大的標準。為C增加了很多東西&#xff0c;其中有一部分是有用的&#xff0c;有一部分是我自認為作用不是很大東西。這一章呢&#xff1f;我們就來說說C11我&#xff0c;我認為對性能…

基于MCU實現的電機轉速精確控制方案:軟件設計與實現

本文將詳細介紹一篇基于微控制器&#xff08;MCU&#xff09;的電機轉速精確控制的軟件方案。通過采樣PWM信號控制和ADC采樣技術&#xff0c;結合PID閉環控制算法&#xff0c;實現了電機轉速的高效、穩定調節。以下是軟件方案流程圖&#xff0c;下文將對其進行展開講解。 原圖太…

Jmeter觸發腳本備份

JMeter 在以下情況會觸發腳本備份&#xff1a; 手動保存測試計劃時&#xff1a;如果測試計劃有未保存的修改&#xff0c;當用戶手動保存測試計劃&#xff08;腳本&#xff09;時&#xff0c;JMeter 都會自動將當前腳本備份到${JMETER_HOME}/backups文件夾下。 關閉 JMeter 時…

AI人工智能-PyCharm的介紹安裝應用

下載與安裝 創建python項目 項目路徑&#xff1a;C:\Users\miloq\Desktop\python_project 配置環境 提前找到conda配置的python-base路徑 配置conda環境 運行項目 運行結果

Flink內存模型--flink1.19.1

Flink 的 JobManager 和 TaskManager 在內存分配上有不同的職責和結構。以下是兩者的內存分類及詳細說明&#xff1a; 一、JobManager 內存分類 JobManager 主要負責作業調度、協調&#xff08;如 Checkpoint 協調&#xff09;、資源管理等&#xff0c;其內存需求相對較低&…