c# 經(jīng)驗談:巧用Expression表達(dá)式 解決類似于sql中 select in 的查詢(適合于中小型項目)
我們在項目經(jīng)常會碰到一些特殊需求 例如下拉框是復(fù)選的,查詢條件是根據(jù)下拉框中復(fù)選項進(jìn)行拼接

看到此圖后大家肯定會說,這很簡單嘛
將所有的選項 拼成“'1-3','5-9'” 然后放到 in 的字句后面,一查就出來了。
這樣做的確在邏輯上沒有問題,可是大家有沒有想過這個問題,過度的和業(yè)務(wù)耦合雖然能夠解決
現(xiàn)在的需求但是卻犧牲了代碼優(yōu)雅和可維護(hù)性
其實本文的目的是想利用Expression表達(dá)式在linq查詢中實現(xiàn)一個優(yōu)雅的解決方案,
同時也會給大家一個用Expression去拼接sql的思路
先上代碼
public static Expression<Func<T, bool>> GetConditionExpression<T>(string[] options, string fieldName)
{
ParameterExpression left = Expression.Parameter(typeof(T), "c");//c=>
Expression expression = Expression.Constant(false);
foreach (var optionName in options)
{
Expression right = Expression.Call
(
Expression.Property(left, typeof(T).GetProperty(fieldName)), //c.DataSourceName
typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 反射使用.Contains()方法
s Expression.Constant(optionName) // .Contains(optionName)
);
expression = Expression.Or(right, expression);//c.DataSourceName.contain("") || c.DataSourceName.contain("")
}
Expression<Func<T, bool>> finalExpression
= Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });
return finalExpression;
}
我想用逆推的方式說明下這段代碼,其實我們查詢的目的要實現(xiàn)這樣的效果 , someList.where(c=>c.Name.contains("someName")||c.Name.Contains("someName")||...)
1. 首先我們要確定返回什么樣的表達(dá)式,根據(jù)經(jīng)驗.where后面是需要一個Expression<Func<T, bool>> 這樣的一個表達(dá)式,所以方法的返回類型已經(jīng)能確定下來了
2. 接下來的任務(wù)是拼接類似于c=>c.Name.Contains("") 這樣的表達(dá)式,按照自左向右的原則,左側(cè)表達(dá)式參數(shù)c很好理解 就是T,那么這個表達(dá)式的參數(shù)也就搞定了,
可以用Expression.Parameter方法來實現(xiàn),該方法目的是將類型反射并且映射給表達(dá)式中的匿名變量 “c” (也可以理解成將參數(shù)常量封裝成表達(dá)式)
3. 接著是表達(dá)式右側(cè)的拼接
再次仔細(xì)看下這段代碼
Expression right = Expression.Call
(
Expression.Property(left, typeof(T).GetProperty(fieldName)), //c.DataSourceName 首先是反射獲取c的一個屬性
typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 聲明一個string.Contains的方法 c.DataSourceName.Contains() 反射使用.Contains()方法
Expression.Constant(optionName) // c.DataSourceName.Contains(optionName) 封裝常量
);
為什么要使用Expression.Call ?
(因為c.Name.contains 屬于string.contains()這個方法所以我們必須將該方法封裝成表達(dá)式,Expression.Call的功能就是將方法封裝成表達(dá)式)
這時候大家會問contains什么呢? 當(dāng)然常量option雖然是string類型,但是仍需封裝成表達(dá)式,Expression.Constant(optionName) 起到了封裝常量的作用
于是c=>c.屬性.Contains(常量) 這個表達(dá)式搞定,可是還是有問題:怎么加上“||” ,聰明的你已經(jīng)有了答案,Expression.Or()
4 最后一步當(dāng)然非常關(guān)鍵,就像產(chǎn)品需要通過流水線進(jìn)行包裝組合,表達(dá)式也不例外:
Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });
對于整個表達(dá)式來說,左側(cè)是參數(shù)表達(dá)式(ParameterExpression),Expression.Lambda就是=>符號,就右側(cè)表達(dá)式和參數(shù)表達(dá)式通過lambda符號進(jìn)行組合,搞定
這樣的話,你只需傳入一個字符串?dāng)?shù)組就能在Linq中實現(xiàn)類似于sql中select in 的效果了,
很多朋友肯定會問,既然能夠用自定義表達(dá)式搞定,那么可不可以將表達(dá)式的思路用于拼接sql?
答案是肯定的。但是如果業(yè)務(wù)邏輯非常復(fù)雜,而且難以把握,還是建議用ado 配合存過實現(xiàn)
感謝女友一直陪到深夜,讓我堅持把這篇博文寫完,文中如有錯誤,也請大家海涵并且能夠及時告訴我,謝謝!
浙公網(wǎng)安備 33010602011771號