亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C# Entity Framework中的IQueryable和IQueryProvider詳解

2020-01-24 02:09:56
字體:
來源:轉載
供稿:網友

前言

      相信大家對Entity Framework一定不陌生,我相信其中Linq To Sql是其最大的亮點之一,但是我們一直使用到現在卻不曾明白內部是如何實現的,今天我們就簡單的介紹IQueryable和IQueryProvider。

 IQueryable接口

      我們先聊聊這個接口,因為我們在使用EF中經??吹絣inq to sql語句的返回類型是IQueryable,我們可以看下這個接口的結構:

復制代碼 代碼如下:

public interface IQueryable : IEnumerable
{
      Type ElementType { get; }
      Expression Expression { get; }
      IQueryProvider Provider { get; }
}
 

      或許會有人很奇怪,當我們在開發過程中使用這個接口的時候,提供的方法遠遠不止這么點,因為微軟提供了強大的Queryable類,當然大家不要以為這個類是實現IQueryable然后實現了很多方法,如果是那樣那些第三方庫怎么自定義呢?所以Queryable只是一個靜態類,對IQueryable接口進行了擴展,下面是筆者在.Net Reflector截圖中一部分:

       如果讀者細心一點會發現linq to sql并不會導致實際的查詢,只有當我們真正開始使用的時候才從數據庫中開始查詢數據。

IQueryProvider接口

      如果我們調試的EF的話,會看到生成的T-SQL語句。T-SQL就是根據表達式樹分析從而得出的,而核心就是IQueryProvider接口,下面就是該接口的結構:

復制代碼 代碼如下:

public interface IQueryProvider
{
        IQueryable CreateQuery(Expression expression);
        IQueryable<TElement> CreateQuery<TElement>(Expression expression);
        object Execute(Expression expression);
        TResult Execute<TResult>(Expression expression);
}
 

      其中CreateQuery就是負責解析表達式樹的,當然還要將處理后的結果返回,以便接著分析下面的語句,當然這中間只是分析,你完全可以根據表達式樹得出你自己需要的查詢語句,比如SQL或者其他什么,只有在真正使用數據的時候才會調用Execute方法,這個時候就可以根據我們自己分析的語句開始進行實際的查詢了。

 實例分析

QueryProvider類

      光說不練我們永遠不能明白其中的原理,所以下面我們就簡單的舉一個例子來展示下。首先我們先實現IQueryProvider接口,其中會用到一個Query類,這個類會在后面進行介紹,首先我們新建一個QueryProvider類實現IQueryProvider接口,首先我們看下CreateQuery<S>方法:

       這里的expression就是傳遞給我們,并且需要我們處理的表達式樹,最后還要返回實現IQueryable<S>接口的示例,以便LINQ在此基礎上進行下面的查詢,這里我們僅僅只是創建了一個Query的實例,同時將expression傳遞給它,因為此處僅僅只是一個DEMO,所以我們沒有去真正解析表達式樹(這其中要做的工作很多)。接著還有CreateQuery方法:

 我們可以看到下面這句話:

實際的含義就是創建Query<>的實例,并且泛型參數是elementType,參數是thisexpression。

 最后就是Execute方法了,傳遞一個Expression參數,并獲取最后的結果,筆者在這里直接是寫死的值:

 Query類

      僅僅只有QueryProvider還沒用,我們還需要一個能夠保存表達式樹狀態的類,當然也包括了我們解析表達式后的結果也可以保存在其中,這樣我們在IQueryProvider的Execute方法中就可以根據我們解析的結果執行執行并返回結果了。

       這里我們可以看到Query的Expression值在創建這個實例時,如果沒有傳遞Expression參數時該值就是:

       但是在后面的過程中Query中的Expression將是QueryProvider中的expression值。

       到此我們其實就完成了一個簡單的示例了,我們就可以開始測試我們的成果了,筆者在利用如下的代碼來測試:

      OK,我們開始看看是如何分析這句LINQ語句的。

       首先我們看下在一開始執行時Query中Expression的返回值(如下圖):

      在獲取到這個表達式后,就開始執行Linq,首先執行的是where item == 123。

 分析Where item == 123

     接著我們F5,就可以看到在QueryProvider中的CreateQuery<S>命中了,并且Expression參數如下圖所示:

      我們看到里面的字符串是 Where(item => (item == 123)),通過這句話我們就可以明白其實LINQ中的where實質上就是利用Where方法,并傳遞給它對應的lambda表達式。分析完了where部分,下面就是FirstOrDefault部分了。

 分析FirstOrDefault

      當執行到FirstOrDefault的時候我們可以查看t的值,會發現t實際上就是QueryProvider中CreateQuery<S>的返回值。

 

      接著我們開始執行下面FirstOrDefault方法,發現會再一次的去獲取Expression的值,而此時Expression的值就是上面CreateQuery<T>傳遞給我們的參數expression。

      然后在將這個表達式樹和由表達式樹表示FirstOrDefault方法調用的值拼接起來,并調用QueryProvider中的Execute<S>方法,我們可以看到這個時候傳遞給我們的參數expression的值。

       至此一個簡單的流程就結束了,最后就是返回筆者寫死的123這個值了。

       通過上面這個例子我們基本了解了其工作的流程,下面我們將一步一步的分析我們這個where item == 123,當然我們將會用到遞歸,所以請大家整理好自己的思路,一步一步的看如何從一個表達式樹中分析這條語句。

 分析表達式樹實戰

      首先我們一個分析表達式樹的方法,這個方法我們暫且放在QueryProvider中:

復制代碼 代碼如下:

public void AnalysisExpression(Expression exp)
        {
            switch (exp.NodeType)
            {
                case ExpressionType.Call:
                    {
                        MethodCallExpression mce = exp as MethodCallExpression;
                        Console.WriteLine("The Method Is {0}", mce.Method.Name);
                        for (int i = 0; i < mce.Arguments.Count; i++)
                        {
                            AnalysisExpression(mce.Arguments[i]);
                        }
                    }
                    break;
                case ExpressionType.Quote:
                    {
                        UnaryExpression ue = exp as UnaryExpression;
                        AnalysisExpression(ue.Operand);
                    }
                    break;
                case ExpressionType.Lambda:
                    {
                        LambdaExpression le = exp as LambdaExpression;
                        AnalysisExpression(le.Body);
                    }
                    break;
                case ExpressionType.Equal:
                    {
                        BinaryExpression be = exp as BinaryExpression;
                        Console.WriteLine("The Method Is {0}", exp.NodeType.ToString());
                        AnalysisExpression(be.Left);
                        AnalysisExpression(be.Right);
                    }
                    break;
                case ExpressionType.Constant:
                    {
                        ConstantExpression ce = exp as ConstantExpression;
                        Console.WriteLine("The Value Type Is {0}", ce.Value.ToString());
                    }
                    break;
                case ExpressionType.Parameter:
                    {
                        ParameterExpression pe = exp as ParameterExpression;
                        Console.WriteLine("The Parameter Is {0}", pe.Name);
                    }
                    break;
                default:
                    {
                        Console.Write("UnKnow");
                    }
                    break;
            }
        }
 

并在CreateQuery<S>中調用這個方法

      然后我們可以開始運行測試了,為了能夠讓讀者明白當前處理的表達式樹,所以在下面的截圖中將會包含AnalysisExpression中參數exp的值,這樣可以便于讀者區分當前處理的表達式樹。

 PS:Expression類型中的NodeType是非常重要的,因為傳遞給我們的都是父類Expression類型,而我們需要根據NodeType的轉換成對應的子類,這樣我們才能夠獲取到更詳細的信息。

 ExpressionType.Call

      我們根據一開始的exp的NodeType進入到這個分支,因為where實質上就是ss調用where方法,所以我們通過將exp轉換成對應的MethodCallExpression類型,這樣我們就可以看到調用的方法名稱了。

      當然調用一個方法必須要有參數,所以下面還需要循環Arguments去分析具體的參數,其中也包括調用這個方法的對象,自然我們首先是分析調用這個方法的對象,這里我們進行了第一次的遞歸調用,跳到了ExpressionType.Constant。

 ExpressionType.Constant

      NodeType為這個類型,我們就可以通過ConstantExpression類型來獲取對應的參數,通過Value我們可以可以獲取到調用where方法的對象,當然到這里就不會繼續往下分析了。

 

    所以我們繼續跳到之前的for循環,開始分析第二個參數,就是 item => item == 123這個部分了。

 ExpressionType.Quote

      如果接觸過lambda的人可能會認為類型應該是Lambda,但實際上不會直接跳轉到那,而是先跳轉到Quote,然后我們再把轉換成UnaryExpression類型,然后再繼續分析其中Operand屬性,而這個屬性的NodeType就是Lambda了。個人認為這個應該是區分lambda和普通的方法,因為where不僅僅可以接收lambda同時也可以是常規的方法,所以這里還需要這一層。

 ExpressionType.Lambda

跳轉到這,大家就不會感覺奇怪了,這里為了簡潔。筆者并沒有分析參數,而是直接分析Body部分,因為這部分才是我們的關鍵。

 ExpressionType.Equal

      我們看到這個lambda很簡單,就是一個相等比較,所以直接跳轉到了Equal,當然還有And、Or等對應的枚舉,而到了這一步我們就可以直接分析Left和Right,當然這里還有一個小插曲,就是在跳到這個枚舉的時候我查看exp的類型時,實際上是LogicalBinaryExpression類型,并不是BinaryExpression類型,然后用Reflector查看了下,我就呵呵了。

       我當時還奇怪,怎么沒有這個類型呢,最后才知道玩的是這一出。到此為止,我們繼續分析這個相等操作的左右兩邊的參數吧。

首先分析的是左邊參數item。

 ExpressionType.Parameter

      Item挑傳到這,并將其轉換成ParameterExpression類型,筆者在此僅僅只輸出了參數的名稱。

到這左邊的參數分析完畢,我們開始分析右邊的參數。

 ExpressionType.Constant

      我們可以輕松的想到對應的Value就是123了,到此整個表達式就分析完畢了。

 我們看看最后控制臺的輸出結果吧。

       在此筆者還要聲明一個問題,就是我們應該去理解我們使用的各種庫的原理,這樣便于我們以后添加符合實際開發的一些功能,當然這并不是浪費時間。而是提高今后項目開發的時間,隨著不斷的積累,我們會發現很多重復的功能并不需要我們去重復寫了,而節省下來的時間我們就可以做自己想做的事了,所以我們要做一個有思想的懶程序員。

 源碼下載

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品一区二区亚洲| 欧美在线一区二区三区四| 亚洲精品久久久久久久久久久| 国产精品网站大全| 亚洲欧美资源在线| 欧美日韩成人黄色| 成人精品一区二区三区| 亚洲tv在线观看| 2019中文字幕全在线观看| 欧美诱惑福利视频| 国产999视频| 91精品视频一区| 亚洲天堂成人在线视频| 国产精品久久久久久影视| 欧美二区在线播放| 日韩av一区二区在线观看| 伊人久久综合97精品| 日韩高清av一区二区三区| 成人妇女淫片aaaa视频| 国产欧美va欧美va香蕉在线| 欧美性理论片在线观看片免费| 国产精品69久久久久| 日韩欧美黄色动漫| 亚洲免费av电影| 亚洲性生活视频在线观看| 欧美日本黄视频| 98精品国产自产在线观看| 国产女人精品视频| 久久久久999| 亚洲高清色综合| 欧美国产高跟鞋裸体秀xxxhd| 性欧美xxxx交| 国产精品免费在线免费| 国产丝袜视频一区| 国产欧美精品日韩精品| 亚洲精选一区二区| 九九热99久久久国产盗摄| 欧美午夜久久久| 亚洲成人精品在线| 国产亚洲欧美另类中文| 国产成人精品免高潮在线观看| 精品国产999| 色av吧综合网| 欧美激情在线狂野欧美精品| 色樱桃影院亚洲精品影院| 日韩美女视频中文字幕| 亚洲图片在线综合| 夜夜嗨av色一区二区不卡| 色综合久久中文字幕综合网小说| 高清一区二区三区日本久| 日韩欧美极品在线观看| 亚洲综合精品伊人久久| 午夜精品福利视频| 91精品国产综合久久香蕉的用户体验| 毛片精品免费在线观看| 国产精品久久久久久久久久三级| 久久久久久亚洲| 欧洲精品毛片网站| 不用播放器成人网| 九色精品免费永久在线| 91丝袜美腿美女视频网站| 国产一区二区三区中文| 欧美在线视频免费观看| 亚洲欧美制服丝袜| 成人黄色av播放免费| 国产精品第一页在线| 欧美成人精品在线| 国产精品欧美激情| 日韩精品欧美国产精品忘忧草| 久久九九精品99国产精品| 国产精品一区专区欧美日韩| 亚洲护士老师的毛茸茸最新章节| 最近的2019中文字幕免费一页| 成人黄色免费看| 亚洲性69xxxbbb| 海角国产乱辈乱精品视频| 亚洲一区亚洲二区| 国产视频精品自拍| 国产精品视频一| www日韩欧美| 91精品啪aⅴ在线观看国产| 久久99国产精品自在自在app| 麻豆成人在线看| 欧美在线一区二区三区四| 18一19gay欧美视频网站| 国内精品久久久久久影视8| 久久精品国产精品亚洲| 尤物九九久久国产精品的分类| 中文字幕少妇一区二区三区| 欧美激情一区二区三区久久久| 国产ts一区二区| 欧美日韩国产一区中文午夜| 色综合久久精品亚洲国产| www国产亚洲精品久久网站| 国产在线播放不卡| 日本一欧美一欧美一亚洲视频| 一本一道久久a久久精品逆3p| 国产亚洲精品成人av久久ww| 色多多国产成人永久免费网站| 亚洲精品美女在线观看| 亚洲欧美激情四射在线日| 国产精品老女人视频| 欧美激情在线有限公司| 欧美日韩美女在线| 第一福利永久视频精品| 日韩大片免费观看视频播放| 欧美日韩人人澡狠狠躁视频| 在线观看欧美视频| 欧美激情视频在线观看| 日韩有码视频在线| 亚洲国产婷婷香蕉久久久久久| 国产99视频精品免视看7| 久久中国妇女中文字幕| 色吧影院999| 欧美日韩国产限制| 日韩成人中文电影| 欧美黄色免费网站| 97在线视频免费| 日韩中文字幕视频在线观看| 国产精品678| 欧美巨猛xxxx猛交黑人97人| 亚洲国产小视频在线观看| 欧美午夜精品在线| 精品国产美女在线| 亚洲精品网站在线播放gif| 青草青草久热精品视频在线网站| 久久免费精品日本久久中文字幕| 人人做人人澡人人爽欧美| 精品久久久久久中文字幕一区奶水| 欧美裸体男粗大视频在线观看| 亚洲精品91美女久久久久久久| 亚洲精品一区二区三区婷婷月| 91成人天堂久久成人| 国产亚洲人成a一在线v站| 成人国产精品一区| 一本色道久久综合狠狠躁篇怎么玩| 91精品国产沙发| 国产精品专区第二| 国产欧美日韩最新| 91精品在线一区| 国产91精品久久久久| 色综合亚洲精品激情狠狠| 国产成人精品久久二区二区| 国产精品看片资源| 自拍亚洲一区欧美另类| 国产美女91呻吟求| 亚洲欧美日韩中文视频| 欧美日韩综合视频| 亚洲最新av网址| 日韩专区在线观看| 久久九九热免费视频| 欧美激情女人20p| 久久综合国产精品台湾中文娱乐网| xxx成人少妇69| 亚洲精品久久久久久下一站| 亚洲黄在线观看| 亚洲已满18点击进入在线看片| 亚洲va电影大全| 成人www视频在线观看| 国产午夜精品麻豆| 亚洲一区二区久久久久久久| 亚洲激情在线观看视频免费| 亚洲91av视频| 亚洲国产精品专区久久|