.NET深刻解析LINQ框架(六:LINQ執行表達式)

在看本篇文章以前我假設您已經具有我以前分析的一些原理知識,由於這章所要講的內容是創建在以前的一系列知識點之上的,爲了保證您的閱讀順利建議您先閱讀本人的LINQ系列文章的前幾篇或者您已經具有比較深刻的LINQ原理知識體系,防止耽誤您的寶貴時間。express

到目前爲止咱們對LINQ的執行原理已經很清楚了,從它的前期構想到它真正爲咱們所用都有足夠的證據,可是彷佛問題並無咱們想的那麼簡單,問題老是在咱們使用中頻頻出現尤爲是新技術的使用,固然有問題纔能有進步。[王清培版權全部,轉載請給出署名]ide

一:LINQ執行表達式

在研究LINQ的過程當中,參考了不少技術文章還有技術書籍,毫無疑問的是Linq to Provider的調用入口都是將Lambda表達式解析成Expression<T>表達式對象,跟Linq to Object不一樣,Linq to Object是將Lambda直接解析成泛型Func類型的委託,可是咱們不少人包括我本身都忽視了一個很大的細節,就是Provider在內部將對Expression<T>進行執行,並不是咱們所理解的那樣將表達式Expression<T>對象徹底解析成等價的SQL,也就是說Expression<T>並非咱們說看到的那樣單純,它具備雙重上下文邏輯在裏面。工具

咱們都是直接使用LINQ做爲查詢接口,VS在最後編譯的時候負責對LINQ的語法進行解析而且翻譯成對應的擴展方法調用。咱們忽視一個重要的環節,就是VS對LINQ進行解析翻譯的時候是會執行LINQ表達式的,這點很是重要。以前我一直覺得VS只負責將LINQ的表達式翻譯成等價的擴展方法調用,後來發現VS爲了知足咱們在前期沒法肯定對象條件的狀況下進行Where字句的拼接,容許咱們在編寫LINQ語句的時候帶有邏輯判斷表達式在裏面,這個功能對咱們進行多條件組合查詢時至關方便,不須要在進行IF、ELSE的多個判斷,只須要順其天然的在LINQ中的第一個表達式中進行判斷就好了。追求優雅代碼的同志很不但願在一個既有LINQ查詢又帶有鏈式查詢的方法中用兩種查詢方式,若是LINQ能知足大部分的查詢功能那最完美;[王清培版權全部,轉載請給出署名]翻譯

爲了說明LINQ在編譯時會被VS執行,咱們用LINQPad工具看一下便知;3d

LINQ查詢表達式:from truck in TB_CX_TRUCKs where 1==1 select truck對象

LINQ等價的鏈式方法: TB_CX_TRUCKs.Where (truck => True)blog

圖1:接口

1

若是沒有執行按道理是直接解析成Lambda的格式(truck)=>1==1纔對,而後讓LINQ to Provider提供程序負責處理纔對,也許以爲沒有實質的意思反正是恆等的表達式因此解析成這樣。咱們在換一種寫法看看;[王清培版權全部,轉載請給出署名]get

LINQ查詢表達式:from truck in TB_CX_TRUCKs where string.IsNullOrEmpty("1111") select truckstring

LINQ等價的鏈式方法:TB_CX_TRUCKs.Where (truck => String.IsNullOrEmpty ("1111"))

圖2:

2

由此能夠得出一個結論,LINQ語句是會被執行和解析的兩個動做,在尚未進入到提供程序時已經能夠看出LINQ是能夠附帶一些執行邏輯在裏面的,而不是最終的SQL執行邏輯。

表達式的處理能夠分爲常量表達式和動態變量表達式,常量表達式在VS編譯的時候就能夠直接計算表達式是不是true、false。而動態變量表達式則須要在後期進行表達式解析的時候計算的,換句話說Linq to Provider中的Provider提供程序是具備高智商的表達式執行器,不只僅是對錶達式等價解析中間還夾雜着對錶達式解析的自定義邏輯代碼。[王清培版權全部,轉載請給出署名]

打個比方,咱們都有過拼接查詢條件的經歷,界面上有N個查詢條件字段,須要根據用戶是否填寫了哪一個字段進行動態的拼接進LINQ語句中去。通常咱們都會進行if的判斷才行,由於咱們都以爲Where後面的條件表達式是直接被解析成對應邏輯的SQL語句,因此只要拼接進去的都是被解析成SQL的Where子句。因爲LINQ是沒法拆分開來進行組裝的,必須一次寫完才能經過編譯。因此咱們都在使用着查詢擴展方法進行數據查詢,這樣的困境使咱們沒法看到LINQ的優雅,反而一直用不到。

經過觀察LINQPad工具解析的SQL語句,發現LINQ查詢表達式在提供程序內部將被執行、解析兩個過程,跟VS的過程是同樣的,能執行先執行,而後解析,解析是創建在前期執行事後的基礎上的。咱們仍是來看一個比較簡單的LINQ解析後的SQL和鏈式方法;[王清培版權全部,轉載請給出署名]

LINQ查詢表達式:from truck in TB_CX_TRUCKs where 1==1 ||truck.LICENSE_NUMBER.Length<10 select truck

LINQ等價的鏈式方法:TB_CX_TRUCKs.Where (truck => (True || (truck.LICENSE_NUMBER.Length < 10)))

圖3:

3

對照鏈式方法,很明顯VS先對1==1表達式進行了執行並返回true做爲後面整個表達式的一部分拼接進Where鏈式方法,因此先執行再解析兩個過程。而後咱們對最後的SQL進行分析,沒有看見任何Where語句,爲何呢?是由於提供程序在內部對錶達式進行了執行並分析了咱們想要的輸出結果,也不知道這樣的效果是否是爲了知足咱們多條件拼接的問題。

因爲Where方法裏面的Lambda表達若是被執行的話,那麼將不會執行(truck.LICENSE-NUMBER.Length<10),因此這點爲咱們的多條件拼接提供了接口。[王清培版權全部,轉載請給出署名]

咱們看一下多條件組合查詢示例:

4

將界面上的查詢實體傳入到數據訪問層以後:

public List<Truck> GetList(Truck truckModel)
{
    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確實很優美,比起以前的IFELSE判斷也省事不少。 

IQueryable<TB_CX_TRUCK> queryList = DbContext.TB_CX_TRUCK.AsQueryable();//初始化一個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));

若是有不少個查詢條件,那麼咱們將要寫不少這樣的判斷代碼,即不方便也不美觀。[王清培版權全部,轉載請給出署名]

 5

多條件之間的OR查詢

儘管不少場合下咱們都是使用Linq中的where關鍵字來拼接查詢條件,可是有一種需求Linq查詢確實知足不了咱們,那就是多條件之間是OR的關係。由於只要咱們用Linq或者鏈式方法出來的寫出來的SQL語句中的where條件後面將都是and關係,這個時候咱們只能用鏈式方法來進行拆分才行。

public List<DutyModel> GetList(DutyModel dutyModel)
{
    using (UserOrgDemo2Entities Context = new UserOrgDemo2Entities())
    {
        IQueryable<TB_DUTY> result = Context.TB_DUTY.AsQueryable();
        System.Linq.Expressions.Expression<Func<TB_DUTY, bool>> expressionDUTY = DynamicLinqExpressions.True<TB_DUTY>();

        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();
    }
}

這裏有個重點就是老外(估計是比較厲害的前輩,在此謝謝了!)寫的一個*.cs文件,裏面是Expression<T>表達式文件的擴展方法,主要就是用來進行多條件Or、And之間組合查詢用的。

全部說若是多條件組合查詢之間是and關係能夠直接使用Linq,若是是or或者是or與and一塊兒,那麼可使用上面這種鏈式查詢方法。

總結:其實說了那麼多目的只有一個,LINQ的解析過程並不是只有一個「提供程序翻譯成SQL」的過程,而是包括了兩個階段,四個過程的處理,LINQ的寫法不少種,原理應該是差很少的,只要咱們在寫LINQ的時候綜合考慮這幾個處理過程,應該對咱們應對複雜的查詢頗有幫助。[王清培版權全部,轉載請給出署名]

相關文章
相關標籤/搜索