c# 经验谈:巧用Expression表达式 解决类似于sql中 select in 的查询(适合于中小型项目)...
我們?cè)陧?xiàng)目經(jīng)常會(huì)碰到一些特殊需求 例如下拉框是復(fù)選的,查詢條件是根據(jù)下拉框中復(fù)選項(xiàng)進(jìn)行拼接
看到此圖后大家肯定會(huì)說(shuō),這很簡(jiǎn)單嘛
將所有的選項(xiàng) 拼成“'1-3','5-9'”? 然后放到 in 的字句后面,一查就出來(lái)了。
這樣做的確在邏輯上沒(méi)有問(wèn)題,可是大家有沒(méi)有想過(guò)這個(gè)問(wèn)題,過(guò)度的和業(yè)務(wù)耦合雖然能夠解決
現(xiàn)在的需求但是卻犧牲了代碼優(yōu)雅和可維護(hù)性
?
其實(shí)本文的目的是想利用Expression表達(dá)式在linq查詢中實(shí)現(xiàn)一個(gè)優(yōu)雅的解決方案,
同時(shí)也會(huì)給大家一個(gè)用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;
}
我想用逆推的方式說(shuō)明下這段代碼,其實(shí)我們查詢的目的要實(shí)現(xiàn)這樣的效果 , someList.where(c=>c.Name.contains("someName")||c.Name.Contains("someName")||...)
1. 首先我們要確定返回什么樣的表達(dá)式,根據(jù)經(jīng)驗(yàn).where后面是需要一個(gè)Expression<Func<T, bool>> 這樣的一個(gè)表達(dá)式,所以方法的返回類(lèi)型已經(jīng)能確定下來(lái)了
2. 接下來(lái)的任務(wù)是拼接類(lèi)似于c=>c.Name.Contains("") 這樣的表達(dá)式,按照自左向右的原則,左側(cè)表達(dá)式參數(shù)c很好理解 就是T,那么這個(gè)表達(dá)式的參數(shù)也就搞定了,
可以用Expression.Parameter方法來(lái)實(shí)現(xiàn),該方法目的是將類(lèi)型反射并且映射給表達(dá)式中的匿名變量 “c” (也可以理解成將參數(shù)常量封裝成表達(dá)式)
3. 接著是表達(dá)式右側(cè)的拼接
?再次仔細(xì)看下這段代碼
Expression right = Expression.Call?
?(??????????????????????????
???? Expression.Property(left, typeof(T).GetProperty(fieldName)),? //c.DataSourceName?????首先是反射獲取c的一個(gè)屬性????????????????
???? typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 聲明一個(gè)string.Contains的方法?????c.DataSourceName.Contains()????????????????反射使用.Contains()方法??
???? Expression.Constant(optionName)?????????? // ?c.DataSourceName.Contains(optionName)???????????????封裝常量???????
?);
? 為什么要使用Expression.Call ?
(因?yàn)?/span>c.Name.contains 屬于string.contains()這個(gè)方法所以我們必須將該方法封裝成表達(dá)式,Expression.Call的功能就是將方法封裝成表達(dá)式)
?這時(shí)候大家會(huì)問(wèn)contains什么呢? 當(dāng)然常量option雖然是string類(lèi)型,但是仍需封裝成表達(dá)式,Expression.Constant(optionName) 起到了封裝常量的作用
?于是c=>c.屬性.Contains(常量) 這個(gè)表達(dá)式搞定,可是還是有問(wèn)題:怎么加上“||” ,聰明的你已經(jīng)有了答案,Expression.Or()
?
4 最后一步當(dāng)然非常關(guān)鍵,就像產(chǎn)品需要通過(guò)流水線進(jìn)行包裝組合,表達(dá)式也不例外:
Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });
對(duì)于整個(gè)表達(dá)式來(lái)說(shuō),左側(cè)是參數(shù)表達(dá)式(ParameterExpression),Expression.Lambda就是=>符號(hào),就右側(cè)表達(dá)式和參數(shù)表達(dá)式通過(guò)lambda符號(hào)進(jìn)行組合,搞定
?
這樣的話,你只需傳入一個(gè)字符串?dāng)?shù)組就能在Linq中實(shí)現(xiàn)類(lèi)似于sql中select in 的效果了,
很多朋友肯定會(huì)問(wèn),既然能夠用自定義表達(dá)式搞定,那么可不可以將表達(dá)式的思路用于拼接sql?
答案是肯定的。但是如果業(yè)務(wù)邏輯非常復(fù)雜,而且難以把握,還是建議用ado?配合存過(guò)實(shí)現(xiàn)
?
?
感謝女友一直陪到深夜,讓我堅(jiān)持把這篇博文寫(xiě)完,文中如有錯(cuò)誤,也請(qǐng)大家海涵并且能夠及時(shí)告訴我,謝謝!
?
?
?
?
?
?
?
?
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/JimmyZheng/archive/2012/02/23/2364154.html
總結(jié)
以上是生活随笔為你收集整理的c# 经验谈:巧用Expression表达式 解决类似于sql中 select in 的查询(适合于中小型项目)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: oracle 基础1
- 下一篇: 总结C#中窗体间传递数据的几种方法