.NET深刻解析LINQ框架(五:IQueryable、IQueryProvider接口詳解)

  • 3.5.環路執行對象模型、碎片化執行模型(假遞歸式調用)

  • 3.6.N層對象執行模型(縱橫向對比鏈式擴展方法)

  • 3.7.LINQ查詢表達式和鏈式查詢方法其實都是空殼子

  • 3.8.詳細的對象結構圖(對象的執行原理)

  • 3.9.IQueryable<T>與IQueryProvider一對一的關係可否改爲一對多的關係

  • 4.完整的自定義查詢

    3.5】. 環路執行對象模型、碎片化執行模型(假遞歸式調用)

    這個主題扯的可能有點遠,可是它關係着整個LINQ框架的設計結構,至少在我尚未搞懂LINQ的本意以前,在我腦海裏一直頻頻出現這樣的模型,這些模型幫助我理解LINQ的設計原理。其實在最先接觸環路模型和碎片化模型是在前兩個月,那個時候有幸接觸企業應用架構方面的知識,裏面就有不少業務碎片化的設計技巧。其實理解這些所謂的設計模型後將大大開闊咱們的眼界,畢竟研究框架是要研究它的設計原理,它的存在必然是爲了解決某一類問題,問題驅動它的設計模型。因此咱們在研究這樣的模型的時候其實已經在不知不覺的理解問題的本質。html

    到底環路執行模型是什麼?它與碎片化之間是什麼關係?假遞歸式調用又是什麼奧祕?這些種種問題咱們必須都要把它解決了才能暢通無阻的去研究下面的東西。其實環路執行、碎片化、假遞歸式都是問題的不一樣角度的稱呼,就比如咱們常常會用依賴倒置、控制反轉、依賴注入這些詞彙去形容設計原則、設計方法同樣,他們都是爲了解決某種問題而存在,經過巧妙的設計來達到很完美的效果。這裏其實也是如此,咱們來一一的分解說明。[王清培版權全部,轉載請給出署名]程序員

    想必沒有人不瞭解遞歸的原理,對遞歸的使用也是很常見的,經過遞歸算法咱們能夠解決一下沒法解決的大問題,經過將大問題分解成多個一樣數據結構的小問題而後讓計算機重複的去計算就好了。最爲常見的就是遍歷樹形結構的對象,如:XML,它的每一個節點都是同樣的對象,因此遞歸調用的方法也是同一個方法,只不過不斷的調用將產生多個調用棧,最後在按照調用順序的反向出棧就得出一個完整的數據結構。算法

    那麼在LINQ中來講,咱們沒法經過一個方法屢次調用來產生咱們想要的表達式樹,一個Where查詢表達式擴展方法可能不只僅是被LINQ查詢表達式所使用,還有可能被ORM的入口方法所使用,好比Update更新的時候就須要Where給出更新的條件,Delete也一樣如此。(固然咱們這裏討論是LINQ背後的設計原理不僅僅針對LINQ的技術,而是某一類問題的通用設計模式。)那麼咱們如何構造出一個相似遞歸但不是遞歸的算法結構,方法1可能被方法2調用,方法2也可能被方法1所調用,這樣的方法不少,N個方法分別表達不一樣的語義,具體的構造看使用者的需求,因此這裏就出現碎片化的概念了,只有碎片化後才能最大程度的重組,既然能重組了就造成了環路的執行模型。很是完美,看似簡單卻深不見底的模型咱們只瞭解到冰山一角而已,在企業架構、領域驅動設計方向都已經有着不少成功的案例,要否則也不會被稱爲設計模式了更爲強大的稱呼是企業應用架構模式纔對。用文字的方式講解計算機程序問題彷佛有點吃力,用代碼+圖形分析的方式來說解最適合咱們程序員的思惟習慣了。下面我用一個簡單的例子再附上一些簡單的圖示來跟你們分享一下這幾個模式語言的關係。express

    你們確定都知道每逢過年過節都會有不少禮品擺放在超市裏商場裏買,可是咱們都知道一個潛規則,就是這些商品的包裝花費了不少功夫,一層套一層,其實裏面的東西可能很不起眼,這也是一種營銷手段吧。咱們暫且無論這裏面是什麼東西,咱們如今要設計一個可以任意進行N層次包裝的模型出來,一件商品左一層右一層的反覆包裝,包裝幾回咱們無論,咱們提供能進行N層包裝的方法出來就好了。[王清培版權全部,轉載請給出署名]設計模式

    這麼簡單的代碼我就不一一解釋了,這裏只是爲了演示而用沒有添加沒用的代碼,省得耽誤你們時間。數據結構

        
        
        
        
    1. /// <summary>   
    2.    /// 商品抽象類   
    3.    /// </summary>   
    4.    public abstract class Merchandise   
    5.    {   
    6.        /// <summary>   
    7.        /// 商品名   
    8.        /// </summary>   
    9.        public string MerchandiseName { getprotected set; }   
    10.        /// <summary>   
    11.        /// 單價   
    12.        /// </summary>   
    13.        public int UnitPrice { getprotected set; }   
    14.        /// <summary>   
    15.        /// 數量   
    16.        /// </summary>   
    17.        public int Number { getprotected set; }   
    18.  
    19.    }   
    20.    /// <summary>   
    21.    /// 蘋果   
    22.    /// </summary>   
    23.    public class Apple : Merchandise   
    24.    {   
    25.        public Apple() { }   
    26.        private void Init()   
    27.        {   
    28.            base.MerchandiseName = "進口泰國蘋果";   
    29.            base.UnitPrice = 8;//8塊錢一個   
    30.            base.Number = 3;//3個一籃裝   
    31.        }   
    32.    }   
    33.    /// <summary>   
    34.    /// 香蕉   
    35.    /// </summary>   
    36.    public class Banana : Merchandise   
    37.    {   
    38.        public Banana() { }   
    39.        private void Init()   
    40.        {   
    41.            base.MerchandiseName = "雲南綠色香蕉";   
    42.            base.UnitPrice = 3;//3塊錢一根   
    43.            base.Number = 10;//10根一籃裝   
    44.        }   
    45.    }   
    46.  
    47.    /// <summary>   
    48.    /// 商品包裝類   
    49.    /// </summary>   
    50.    public static class MerchandisePack   
    51.    {   
    52.        /// <summary>   
    53.        /// 貼一個商標   
    54.        /// </summary>   
    55.        public static Merchandise PackLogo(this Merchandise mer)   
    56.        {   
    57.            return mer;   
    58.        }   
    59.        /// <summary>   
    60.        /// 包裝一個紅色的盒子   
    61.        /// </summary>   
    62.        public static Merchandise PackRedBox(this Merchandise mer)   
    63.        {   
    64.            return mer;   
    65.        }   
    66.        /// <summary>   
    67.        /// 包裝一個藍色的盒子   
    68.        /// </summary>   
    69.        public static Merchandise PackBlueBox(this Merchandise mer)   
    70.        {   
    71.            return mer;   
    72.        }  
    73.  
  • 咱們看關鍵部分的代碼;架構

  •      
         
         
         
    1. Apple apple = new Apple();   
    2.            apple.PackLogo().PackBlueBox().PackRedBox().PackLogo();   
    3.  
    4.            Banana banana = new Banana();   
    5.            banana.PackRedBox().PackBlueBox().PackLogo();  

    這段代碼我想徹底能夠說服咱們,碎片化後體現出來的擴展性是多麼的靈活。apple在一開始的時候都是須要在上面貼一個小logo的,咱們吃蘋果的都知道的。因爲如今是特殊節日,咱們爲了給接收禮品的人一點小小的Surprise,因此商家要求商品都統一的套了幾層包裝,有了這個模型確實方便了不少。app

    徹底實現了獨立擴展的能力,不會將包裝的方法牽扯到領域對象中去,很乾淨明瞭。框架

    7

    經過這種架構模式進行系統開發後,咱們一目瞭然的就知道系統的每個邏輯的過程,更像一種工做流的執行方式,先是什麼而後是什麼。不像在IF ELSE裏面充斥亂七八糟的邏輯,很難維護。不愧爲企業應用架構模式的一種啊。固然LINQ中只有Linq to Object纔會出現重複的使用一到兩個方法來完成功能,像Linq to Entity 幾乎不會出現這種狀況。通常針對查詢的化只是關鍵字存在於不一樣的查詢上下文中,這裏是爲了講解它的背後設計原理。編輯器

    3.6】.N層對象執行模型(縱橫向對比鏈式擴展方法)

    其實原本不打算加這一小節的,可是考慮到確定有部分朋友不是很理解多個對象如何協調的去解決某類問題的。IQueryable<T>接口貌似是一個對象,可是它們都屬於一個完整的IQueryable<T>中的一員。N層對象體如今哪裏?從一開始的IQueryable被擴展方法所處理就已經開始第一層的對象處理,重複性的環路假遞歸似的調用就造成N層對象模型。在LINQ中的查詢表達式與查詢方法實際上是一一對應的,擴展方法是縱向的概念,而LINQ查詢表達式是橫向的,其實二者屬於對應關係。詳情能夠參見本人的「NET深刻解析LINQ框架(四:IQueryable、IQueryProvider接口詳解)」一文;

    3.7】.LINQ查詢表達式和鏈式查詢方法其實都是空殼子

    LINQ的真正意圖是在方便咱們構建表達式樹(ExpressionTree),手動構建過表達式樹的朋友都會以爲很麻煩(對動態表達式有興趣的能夠參見本人的「.NET深刻解析LINQ框架(三:LINQ優雅的前奏)」一文),因此咱們能夠經過直接編寫Lambda的方式調用擴展方法,因爲LambdaExpression也是來自於Expression,而Expression<T>又是來自LambdaExpression,別被這些搞暈,Expression<T>實際上是簡化咱們使用表達式的方式。對於Expression<T>的編譯方式是編輯器幫咱們生成好的,在運行時咱們只管獲取ExpressionTree就好了。LINQ的查詢表達式是經過擴展方法橫向支撐的,你不用LINQ也同樣能夠直接使用各個擴展方法,可是那樣會很麻煩,開發速度會很慢,最大的問題不在於此,而是沒有統一的查詢方式來查詢全部的數據源。LINQ的本意和初衷是提供統一的方式來供咱們查詢全部的數據源,這點很重要。

    3.8】詳細的對象結構圖(對象的執行原理)

    這篇文章的重點就在這一節了,上面說了那麼多的話若是朋友能懂還好不懂的話還真是頭疼。這一節我將給出LINQ的核心的執行圖,咱們將很清楚的看見LINQ的最終表達式樹的對象結構,它是如何構建一棵完整的樹形結構的,IQueryable接口是怎麼和IQueryProvider接口配合的,爲何IQueryable具有延遲加載的能力。文章的最後將給出一個完整的Linq to Provider的小例子,喜歡擴展LINQ的朋友確定會喜歡的。

    8

    上圖看上去可能會很亂,可是靜下心來看仍是能理解的,按照DbQueryable生命週期來看,之上而下,若是有問題能夠回覆評論進一步探討。

     

    3.9】.IQueryable<T>與IQueryProvider一對一的關係可否改爲一對多的關係

    IQueryable對象都有一個配套的IQueryProvider對象,在頻繁的建立IQueryable的時候都會從新建立IQueryProvider對象,畢竟是一種浪費。咱們能夠適當的修改實現IQueryable類的內部結構,讓每次建立IQueryable以後能重用上一次的IQueryProvider的對象,畢竟IQueryProvider對象沒有任何的中間狀態的數據,只是CreateQuery、 Execute兩個方法。這裏只是本人的一點小小的改進想法,不必定須要考慮這些。[王清培版權全部,轉載請給出署名]

    4】.完整的自定義查詢

    LINQ的分析接近尾聲了,這篇文章將是深刻分析LINQ的最後一篇。既然已經結束了LINQ的所有分析,那麼咱們動手寫一個小例子,做爲想擴展LINQ的小雛形。該例子不會涉及到對錶達式樹的分析,畢竟表達式樹的分析並不是易事,後面會有專本的文章來剖析表達式樹的完整結構,這裏只是全盤的IQueryable和IQueryProvider的實現。

    ORM一直是咱們比較喜歡去寫的框架,這裏就使用自定義的IQueryable來查詢相應的對象實體。首先咱們須要繼承IQueryable<T>接口來讓LINQ能查詢咱們本身的數據上下文。

  •  

        
        
        
        
    1. public class DbQuery<T> : IQueryable<T>, IDisposable   
    2.     {   
    3.         public DbQuery()   
    4.         {   
    5.             Provider = new DbQueryProvider();   
    6.             Expression = Expression.Constant(this);//最後一個表達式將是第一IQueryable對象的引用。   
    7.         }   
    8.         public DbQuery(Expression expression)   
    9.         {   
    10.             Provider = new DbQueryProvider();   
    11.             Expression = expression;   
    12.         }   
    13.  
    14.         public Type ElementType   
    15.         {   
    16.             get { return typeof(T); }   
    17.             private set { ElementType = value; }   
    18.         }   
    19.  
    20.         public System.Linq.Expressions.Expression Expression   
    21.         {   
    22.             get;   
    23.             private set;   
    24.         }   
    25.  
    26.         public IQueryProvider Provider   
    27.         {   
    28.             get;   
    29.             private set;   
    30.         }   
    31.  
    32.         public IEnumerator<T> GetEnumerator()   
    33.         {   
    34.             return (Provider.Execute<T>(Expression) as IEnumerable<T>).GetEnumerator();   
    35.         }   
    36.  
    37.         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()   
    38.         {   
    39.             return (Provider.Execute(Expression) as IEnumerable).GetEnumerator();   
    40.         }   
    41.  
    42.         public void Dispose()   
    43.         {   
    44.  
    45.         }   
    46.     }  
    47.  

    下面須要實現提供程序。

        
        
        
        
    1. public class DbQueryProvider : IQueryProvider  
    2.    {  
    3.        public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)  
    4.        {  
    5.            return new DbQuery<TElement>();  
    6.        }  
    7.  
    8.        public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)  
    9.        {  
    10.            //這裏牽扯到對錶達式樹的分析,就很少說了。  
    11.            throw new NotImplementedException();  
    12.        }  
    13.  
    14.        public TResult Execute<TResult>(System.Linq.Expressions.Expression expression)  
    15.        {  
    16.            return default(TResult);//強類型數據集  
    17.        }  
    18.  
    19.        public object Execute(System.Linq.Expressions.Expression expression)  
    20.        {  
    21.            return new List<object>();//弱類型數據集  
    22.        }  
    23.    } 

    咱們看看如何使用;

        
        
        
        
    1. using (DbQuery<Order> dbquery = new DbQuery<Order>())  
    2.            {  
    3.                var OrderList = from order in dbquery where order.OrderName == "111" select order;  
    4.                OrderList.Provider.Execute<List<Order>>(OrderList.Expression);//當即執行  
    5.                foreach (var o in OrderList)  
    6.                {  
    7.                    //延遲執行  
    8.                }  
    9.            } 

    喜歡編寫ORM框架的朋友徹底值得花點時間搞一套本身的LINQ TO ORM,這裏只是讓你高興一下。[王清培版權全部,轉載請給出署名]

相關文章
相關標籤/搜索