c# 動(dòng)態(tài)構(gòu)建LINQ查詢(xún)表達(dá)式
作者:精致碼農(nóng)
聯(lián)系:liam.wang@live.com
最近工作中遇到一個(gè)這樣的需求:在某個(gè)列表查詢(xún)功能中,可以選擇某個(gè)數(shù)字列(如商品單價(jià)、當(dāng)天銷(xiāo)售額、當(dāng)月銷(xiāo)售額等),再選擇 小于或等于 和 大于或等于 ,再填寫(xiě)一個(gè)待比較的數(shù)值,對(duì)數(shù)據(jù)進(jìn)行查詢(xún)過(guò)濾。
如果只有一兩個(gè)這樣的數(shù)字列,那么使用 Entity Framework Core 可以這么寫(xiě) LINQ 查詢(xún):
public Task<List<Product>> GetProductsAsync(string propertyToFilter, MathOperator mathOperator, decimal value)
{
var query = _context.Products.AsNoTracking();
query = propertyToFilter switch
{
"Amount1" when mathOperator == MathOperator.LessThanOrEqual => query.Where(x => x.Amount1 <= value),
"Amount1" when mathOperator == MathOperator.GreaterThanOrEqual => query.Where(x => x.Amount1 >= value),
"Amount2" when mathOperator == MathOperator.LessThanOrEqual => query.Where(x => x.Amount2 <= value),
"Amount2" when mathOperator == MathOperator.GreaterThanOrEqual => query.Where(x => x.Amount2 >= value),
_ => throw new ArgumentException($"不支持 {propertyToFilter} 列作為數(shù)字列查詢(xún)", nameof(propertyToFilter))
};
return query.ToListAsync();
}
如果固定只有一兩個(gè)數(shù)字列且將來(lái)也不會(huì)再擴(kuò)展,這樣寫(xiě)簡(jiǎn)單粗暴,也沒(méi)什么問(wèn)題。
但如果有幾十個(gè)數(shù)字列,這樣使用 swith 模式匹配的寫(xiě)法就太恐怖了,代碼大量重復(fù)。很自然地,我們得想辦法根據(jù)屬性名動(dòng)態(tài)創(chuàng)建 Where 方法的參數(shù)。它的參數(shù)類(lèi)型是:Expression<Func<T, bool>>,是一個(gè)表達(dá)式參數(shù)。
要知道如何動(dòng)態(tài)創(chuàng)建一個(gè)類(lèi)似 Expression<Func<T, bool>> 類(lèi)型的表達(dá)式實(shí)例,就要知道如何拆解表達(dá)式樹(shù)。
對(duì)于本示例,以 x => x.Amount1 <= value 表達(dá)式實(shí)例為例,它的表達(dá)式樹(shù)是這樣的:

然后我們可以按照此表達(dá)式樹(shù)結(jié)構(gòu)來(lái)構(gòu)建我們的 LINQ 表達(dá)式:
public Task<List<Product>> GetProductsAsyncV2(string propertyToFilter, MathOperator mathOperator, decimal value)
{
var query = _context.Products.AsNoTracking();
var paramExp = Expression.Parameter(typeof(Product));
var memberExp = Expression.PropertyOrField(paramExp, propertyToFilter);
var valueExp = Expression.Constant(value);
var compareExp = mathOperator == MathOperator.LessThanOrEqual ?
Expression.LessThanOrEqual(memberExp, valueExp) :
Expression.GreaterThanOrEqual(memberExp, valueExp);
var lambda = Expression.Lambda<Func<Product, bool>>(compareExp, paramExp);
return query.Where(lambda).ToListAsync();
}
每個(gè) Expression.XXX 靜態(tài)方法返回的都是一個(gè)以 Expression 為基類(lèi)的實(shí)例,代表一個(gè)表達(dá)式。不同的表達(dá)式又可以組成一個(gè)新的表達(dá)式,直到得到我們需要的 Lambda 表達(dá)式。這樣就形成了一種樹(shù)形結(jié)構(gòu),我們稱(chēng)為表達(dá)式樹(shù)。知道如何把一個(gè)最終的查詢(xún)表達(dá)式拆解成表達(dá)式樹(shù),我們就容易動(dòng)態(tài)構(gòu)建此查詢(xún)表達(dá)式。
得到一個(gè)表達(dá)式后,我們還可以動(dòng)態(tài)編譯并調(diào)用該表達(dá)式,比如上面示例得到的 lambda 變量,是一個(gè)Expression<Func<Product, bool>> 類(lèi)型,調(diào)用其 Compile 方法,可以得到 Func<Product, bool> 類(lèi)型的委托。
...
var toTestProduct = new Product { Amount1 = 100, Amount2 = 200 };
Func<Product, bool> func = lambda.Compile();
var result = func(toTestProduct);
Console.WriteLine($"The product's {propertyToFilter} is to {mathOperator} {value}.");
// Output: The product's Amount1 is LessThanOrEqual to 150.
你可以通過(guò)研究 Expression 類(lèi)來(lái)了解更多動(dòng)態(tài)構(gòu)建表達(dá)式的方法。
動(dòng)態(tài)構(gòu)建 LINQ 表達(dá)式對(duì)于不能在編譯時(shí)建立查詢(xún),只能在運(yùn)行時(shí)建立查詢(xún)的場(chǎng)景很有用。但它的缺點(diǎn)也很明顯,不易維護(hù)、不易閱讀、不易調(diào)試。如果最終的表達(dá)式執(zhí)行出錯(cuò),很難通過(guò)調(diào)試來(lái)發(fā)現(xiàn)具體是構(gòu)建中的那一步寫(xiě)錯(cuò)了,只能憑自己的理解和經(jīng)驗(yàn)查找錯(cuò)誤。所以,如非必須,一般不推薦動(dòng)態(tài)構(gòu)建 LINQ 查詢(xún)表達(dá)式。
以上就是c# 動(dòng)態(tài)構(gòu)建LINQ查詢(xún)表達(dá)式的詳細(xì)內(nèi)容,更多關(guān)于c# LINQ查詢(xún)表達(dá)式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- C#集合查詢(xún)Linq在項(xiàng)目中使用詳解
- C# Linq延遲查詢(xún)的執(zhí)行實(shí)例代碼
- c# Linq查詢(xún)?cè)斀?/a>
- C#使用LINQ查詢(xún)表達(dá)式的基本子句總結(jié)
- C# linq查詢(xún)之動(dòng)態(tài)OrderBy用法實(shí)例
- C#中Linq延遲查詢(xún)的例子
- C#使用linq語(yǔ)句查詢(xún)數(shù)組中以特定字符開(kāi)頭元素的方法
- C#使用linq查詢(xún)大數(shù)據(jù)集的方法
- C#中Linq查詢(xún)基本操作使用實(shí)例
- C# LINQ查詢(xún)表達(dá)式及對(duì)應(yīng)LAMBDA表達(dá)式的用法
- C#使用LINQ查詢(xún)操作符實(shí)例代碼(一)
相關(guān)文章
C#中關(guān)于zip壓縮解壓幫助類(lèi)的封裝 附源碼下載
之前一個(gè)同學(xué)問(wèn)了這個(gè)問(wèn)題后,看了園子里其它園友的封裝,都很零碎,調(diào)用也不是很方便。所以自己就封裝了一個(gè)zip解壓的類(lèi)。后來(lái)想整理下怕自己忘了。就把壓縮的類(lèi)也一并封裝了2013-02-02
C#實(shí)現(xiàn)簡(jiǎn)易計(jì)算器功能(1)(窗體應(yīng)用)
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)簡(jiǎn)易計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
c#使用windows服務(wù)更新站點(diǎn)地圖的詳細(xì)示例
這篇文章主要介紹了c#使用windows服務(wù)更新站點(diǎn)地圖的詳細(xì)示例,需要的朋友可以參考下2014-04-04
C#實(shí)現(xiàn)類(lèi)似新浪微博長(zhǎng)URL轉(zhuǎn)短地址的方法
這篇文章主要介紹了C#實(shí)現(xiàn)類(lèi)似新浪微博長(zhǎng)URL轉(zhuǎn)短地址的方法,涉及C#操作正則表達(dá)式的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04
C#實(shí)現(xiàn)簡(jiǎn)單播放mp3的方法
這篇文章主要介紹了C#實(shí)現(xiàn)簡(jiǎn)單播放mp3的方法,涉及C#播放多媒體文件的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03

