使用Expression Tree構建動態LINQ查詢


這篇文章介紹一個有意思的話題,也是經常被人問到的:如何構建動態LINQ查詢?所謂動態,主要的意思在於查詢的條件可以隨機組合,動態添加,而不是固定的寫法。這個在很多系統開發過程中是非常有用的。

我這里給的一個解決方案是采用Expression Tree來構建。

其實這個技術很早就有,在.NET Framework 3.5開始引入。之前也有不少同學寫過很多不錯的理論性文章。我自己當年學習這個,覺得最好的幾篇文章是由"裝配腦袋"同學寫的。【有時間請仔細閱讀這些入門指南,做點練習基本就能理解】

Expression Tree上手指南 (一) - 裝配腦袋 - 博客園

Expression Tree 上手指南 (二) - 裝配腦袋 - 博客園

Expression Tree 上手指南 (三) - 裝配腦袋 - 博客園

 

我下面給出的這個實例,希望能幫助大家更加深入理解這個技術,並且結合常見的LINQ to SQL來實現動態的查詢。

下面這個查詢,大家應該都很眼熟

如果我們的條件是固定的,例如上例中,一共有兩個條件,而且條件的邏輯判斷也都是確定的,那么上面這樣寫很容易就能得到我們的結果。

但,問題是,如果我們的條件不是固定的呢?如果你需要根據用戶的選擇,然后動態構造一個查詢呢?

我看過很多人做的一些通用查詢界面,為了應對用戶希望自主選擇條件的這個需求,他們的做法往往就是用"拼接查詢字符串"的做法來實現。這種方法勉強能實現要求,但性能和可維護性方面都相當差。

如果你了解了Expression Tree,那么上面這個查詢可以修改為下面這樣:

 

由此可見,掌握了這個技術的話,那么以后寫動態查詢應該會如虎添翼,至少多了一種很好的思路。

順便說一下,這個技術和反射有點類似,屬於比較底層的技術,掌握了將對大家的編程能力會有所提升。

值得一說的是,就算是我們第一種寫法,內部的實現也是使用Expression Tree來實現的,有興趣的同學可以看看如下的IL代碼。

IL_0001: ldarg.0

IL_0002: call LINQPad.User.TypedDataContext.get_Employees

IL_0007: ldtoken LINQPad.User.Employees

IL_000C: call System.Type.GetTypeFromHandle

IL_0011: ldstr "x"

IL_0016: call System.Linq.Expressions.Expression.Parameter

IL_001B: stloc.1 // CS$0$0000

IL_001C: ldloc.1 // CS$0$0000

IL_001D: ldtoken LINQPad.User.Employees.EmployeeID

IL_0022: call System.Reflection.FieldInfo.GetFieldFromHandle

IL_0027: call System.Linq.Expressions.Expression.Field

IL_002C: ldc.i4.5

IL_002D: box System.Int32

IL_0032: ldtoken System.Int32

IL_0037: call System.Type.GetTypeFromHandle

IL_003C: call System.Linq.Expressions.Expression.Constant

IL_0041: call System.Linq.Expressions.Expression.GreaterThan

IL_0046: ldloc.1 // CS$0$0000

IL_0047: ldtoken LINQPad.User.Employees.Title

IL_004C: call System.Reflection.FieldInfo.GetFieldFromHandle

IL_0051: call System.Linq.Expressions.Expression.Field

IL_0056: ldstr "Sales Representative"

IL_005B: ldtoken System.String

IL_0060: call System.Type.GetTypeFromHandle

IL_0065: call System.Linq.Expressions.Expression.Constant

IL_006A: ldc.i4.0

IL_006B: ldtoken System.String.op_Equality

IL_0070: call System.Reflection.MethodBase.GetMethodFromHandle

IL_0075: castclass System.Reflection.MethodInfo

IL_007A: call System.Linq.Expressions.Expression.Equal

IL_007F: call System.Linq.Expressions.Expression.AndAlso

IL_0084: ldc.i4.1

IL_0085: newarr System.Linq.Expressions.ParameterExpression

IL_008A: stloc.2 // CS$0$0001

IL_008B: ldloc.2 // CS$0$0001

IL_008C: ldc.i4.0

IL_008D: ldloc.1 // CS$0$0000

IL_008E: stelem.ref

IL_008F: ldloc.2 // CS$0$0001

IL_0090: call System.Linq.Expressions.Expression.Lambda

IL_0095: call System.Linq.Queryable.Where

IL_009A: stloc.0 // query

IL_009B: ldloc.0 // query

IL_009C: call LINQPad.Extensions.Dump

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM