這篇文章給大家介紹.NET如何深入解析LINQ框架,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。
從事西云機(jī)房,服務(wù)器租用,云主機(jī),網(wǎng)絡(luò)空間,空間域名,CDN,網(wǎng)絡(luò)代維等服務(wù)。
在研究LINQ的過(guò)程中,參考了很多技術(shù)文章還有技術(shù)書籍,毫無(wú)疑問(wèn)的是Linq to Provider的調(diào)用入口都是將Lambda表達(dá)式解析成Expression
我們都是直接使用LINQ作為查詢接口,VS在最后編譯的時(shí)候負(fù)責(zé)對(duì)LINQ的語(yǔ)法進(jìn)行解析并且翻譯成對(duì)應(yīng)的擴(kuò)展方法調(diào)用。我們忽視一個(gè)重要的環(huán)節(jié),就是VS對(duì)LINQ進(jìn)行解析翻譯的時(shí)候是會(huì)執(zhí)行LINQ表達(dá)式的,這點(diǎn)非常重要。之前我一直以為VS只負(fù)責(zé)將LINQ的表達(dá)式翻譯成等價(jià)的擴(kuò)展方法調(diào)用,后來(lái)發(fā)現(xiàn)VS為了滿足我們?cè)谇捌跓o(wú)法確定對(duì)象條件的情況下進(jìn)行Where字句的拼接,允許我們?cè)诰帉慙INQ語(yǔ)句的時(shí)候帶有邏輯判斷表達(dá)式在里面,這個(gè)功能對(duì)我們進(jìn)行多條件組合查詢時(shí)相當(dāng)方便,不需要在進(jìn)行IF、ELSE的多個(gè)判斷,只需要順其自然的在LINQ中的第一個(gè)表達(dá)式中進(jìn)行判斷就行了。追求優(yōu)雅代碼的同志很不希望在一個(gè)既有LINQ查詢又帶有鏈?zhǔn)讲樵兊姆椒ㄖ杏脙煞N查詢方式,如果LINQ能滿足大部分的查詢功能那最完美;[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]
為了說(shuō)明LINQ在編譯時(shí)會(huì)被VS執(zhí)行,我們用LINQPad工具看一下便知;
LINQ查詢表達(dá)式:from truck in TB_CX_TRUCKs where 1==1 select truck
LINQ等價(jià)的鏈?zhǔn)椒椒ǎ?TB_CX_TRUCKs.Where (truck => True)
圖1:
如果沒(méi)有執(zhí)行按道理是直接解析成Lambda的格式(truck)=>1==1才對(duì),然后讓LINQ to Provider提供程序負(fù)責(zé)處理才對(duì),也許覺(jué)得沒(méi)有實(shí)質(zhì)的意思反正是恒等的表達(dá)式所以解析成這樣。我們?cè)趽Q一種寫法看看;[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]
LINQ查詢表達(dá)式:from truck in TB_CX_TRUCKs where string.IsNullOrEmpty("1111") select truck
LINQ等價(jià)的鏈?zhǔn)椒椒ǎ篢B_CX_TRUCKs.Where (truck => String.IsNullOrEmpty ("1111"))
圖2:
由此可以得出一個(gè)結(jié)論,LINQ語(yǔ)句是會(huì)被執(zhí)行和解析的兩個(gè)動(dòng)作,在還沒(méi)有進(jìn)入到提供程序時(shí)已經(jīng)可以看出LINQ是可以附帶一些執(zhí)行邏輯在里面的,而不是最終的SQL執(zhí)行邏輯。
表達(dá)式的處理可以分為常量表達(dá)式和動(dòng)態(tài)變量表達(dá)式,常量表達(dá)式在VS編譯的時(shí)候就可以直接計(jì)算表達(dá)式是否是true、false。而動(dòng)態(tài)變量表達(dá)式則需要在后期進(jìn)行表達(dá)式解析的時(shí)候計(jì)算的,換句話說(shuō)Linq to Provider中的Provider提供程序是具有高智商的表達(dá)式執(zhí)行器,不僅僅是對(duì)表達(dá)式等價(jià)解析中間還夾雜著對(duì)表達(dá)式解析的自定義邏輯代碼。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]
打個(gè)比方,我們都有過(guò)拼接查詢條件的經(jīng)歷,界面上有N個(gè)查詢條件字段,需要根據(jù)用戶是否填寫了哪個(gè)字段進(jìn)行動(dòng)態(tài)的拼接進(jìn)LINQ語(yǔ)句中去。一般我們都會(huì)進(jìn)行if的判斷才行,因?yàn)槲覀兌加X(jué)得Where后面的條件表達(dá)式是直接被解析成對(duì)應(yīng)邏輯的SQL語(yǔ)句,所以只要拼接進(jìn)去的都是被解析成SQL的Where子句。由于LINQ是無(wú)法拆分開(kāi)來(lái)進(jìn)行組裝的,必須一次寫完才能通過(guò)編譯。所以我們都在使用著查詢擴(kuò)展方法進(jìn)行數(shù)據(jù)查詢,這樣的困境使我們無(wú)法看到LINQ的優(yōu)雅,反而一直用不到。
通過(guò)觀察LINQPad工具解析的SQL語(yǔ)句,發(fā)現(xiàn)LINQ查詢表達(dá)式在提供程序內(nèi)部將被執(zhí)行、解析兩個(gè)過(guò)程,跟VS的過(guò)程是一樣的,能執(zhí)行先執(zhí)行,然后解析,解析是建立在前期執(zhí)行過(guò)后的基礎(chǔ)上的。我們還是來(lái)看一個(gè)比較簡(jiǎn)單的LINQ解析后的SQL和鏈?zhǔn)椒椒?[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]
LINQ查詢表達(dá)式:from truck in TB_CX_TRUCKs where 1==1 ||truck.LICENSE_NUMBER.Length<10 select truck
LINQ等價(jià)的鏈?zhǔn)椒椒ǎ篢B_CX_TRUCKs.Where (truck => (True || (truck.LICENSE_NUMBER.Length < 10)))
圖3:
對(duì)照鏈?zhǔn)椒椒?,很明顯VS先對(duì)1==1表達(dá)式進(jìn)行了執(zhí)行并返回true作為后面整個(gè)表達(dá)式的一部分拼接進(jìn)Where鏈?zhǔn)椒椒?,所以先?zhí)行再解析兩個(gè)過(guò)程。然后我們對(duì)最后的SQL進(jìn)行分析,沒(méi)有看見(jiàn)任何Where語(yǔ)句,為什么呢?是因?yàn)樘峁┏绦蛟趦?nèi)部對(duì)表達(dá)式進(jìn)行了執(zhí)行并分析了我們想要的輸出結(jié)果,也不知道這樣的效果是不是為了滿足我們多條件拼接的問(wèn)題。
由于Where方法里面的Lambda表達(dá)如果被執(zhí)行的話,那么將不會(huì)執(zhí)行(truck.LICENSE-NUMBER.Length<10),所以這點(diǎn)為我們的多條件拼接提供了接口。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]
我們看一下多條件組合查詢示例:
將界面上的查詢實(shí)體傳入到數(shù)據(jù)訪問(wèn)層之后:
public List
{
using (KJtest0817Entities DbContext = new KJtest0817Entities())
{
var resultList = from truck in DbContext.TB_CX_TRUCK
where string.IsNullOrEmpty(truckModel.ENGINE_NUMBER) || truck.ENGINE_NUMBER == truckModel.ENGINE_NUMBER
where string.IsNullOrEmpty(truckModel.LICENSE_NUMBER) || truck.ENGINE_NUMBER == truckModel.LICENSE_NUMBER
select new Truck()
{
BRAND = truck.BRAND
};
return resultList.ToList();
}
}
這樣的查詢LINQ確實(shí)很優(yōu)美,比起之前的IFELSE判斷也省事很多。
IQueryable
if (!string.IsNullOrEmpty(truckModel.LICENSE_NUMBER))
queryList = queryList.Where(truck => truck.LICENSE_NUMBER.Contains(truckModel.LICENSE_NUMBER));
if (!string.IsNullOrEmpty(truckModel.TRUCK_MODEL_CODE))
queryList = queryList.Where(truck => truck.TRUCK_MODEL_CODE.Contains(truckModel.TRUCK_MODEL_CODE));
如果有很多個(gè)查詢條件,那么我們將要寫很多這樣的判斷代碼,即不方便也不美觀。[王清培版權(quán)所有,轉(zhuǎn)載請(qǐng)給出署名]
盡管很多場(chǎng)合下我們都是使用Linq中的where關(guān)鍵字來(lái)拼接查詢條件,但是有一種需求Linq查詢確實(shí)滿足不了我們,那就是多條件之間是OR的關(guān)系。因?yàn)橹灰覀冇肔inq或者鏈?zhǔn)椒椒ǔ鰜?lái)的寫出來(lái)的SQL語(yǔ)句中的where條件后面將都是and關(guān)系,這個(gè)時(shí)候我們只能用鏈?zhǔn)椒椒▉?lái)進(jìn)行拆分才行。
public List
{
using (UserOrgDemo2Entities Context = new UserOrgDemo2Entities())
{
IQueryable
System.Linq.Expressions.Expression
if (!(dutyModel.RANK_NO == 0))
expressionDUTY.Or(duty => duty.RANK_NO == dutyModel.RANK_NO);
if (!string.IsNullOrEmpty(dutyModel.USER_PRIV))
expressionDUTY.Or(duty => duty.USER_PRIV == dutyModel.USER_PRIV);
return result.Where(expressionDUTY).Select(tb_duty=>new DutyModel(){ USER_PRIV=tb_duty.USER_PRIV}).ToList();
}
}
這里有個(gè)重點(diǎn)就是老外(估計(jì)是比較厲害的前輩,在此謝謝了!)寫的一個(gè)*.cs文件,里面是Expression
所有說(shuō)如果多條件組合查詢之間是and關(guān)系可以直接使用Linq,如果是or或者是or與and一起,那么可以使用上面這種鏈?zhǔn)讲樵兎椒ā?/p>
其實(shí)說(shuō)了那么多目的只有一個(gè),LINQ的解析過(guò)程并非只有一個(gè)“提供程序翻譯成SQL”的過(guò)程,而是包括了兩個(gè)階段,四個(gè)過(guò)程的處理,LINQ的寫法很多種,原理應(yīng)該是差不多的,只要我們?cè)趯慙INQ的時(shí)候綜合考慮這幾個(gè)處理過(guò)程,應(yīng)該對(duì)我們應(yīng)對(duì)復(fù)雜的查詢很有幫助。
關(guān)于.NET如何深入解析LINQ框架就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。