.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,这里只是让你高兴一下。[王清培版权全部,转载请给出署名]

相关文章
相关标签/搜索