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

阅读目录:

  • 1.环路执行对象模型、碎片化执行模型(假递归式调用)
  • 2.N层对象执行模型(纵横向对比链式扩展方法)
  • 3.LINQ查询表达式和链式查询方法其实都是空壳子
  • 4.详细的对象结构图(对象的执行原理)
  • 5.IQueryable<T>与IQueryProvider一对一的关系能否改成一对多的关系
  • 6.完整的自定义查询

1】. 环路执行对象模型、碎片化执行模型(假递归式调用)

这个主题扯的可能有点远,但是它关系着整个LINQ框架的设计结构,至少在我还没有搞懂LINQ的本意之前,在我脑海里一直频频出现这样的模型,这些模型帮助我理解LINQ的设计原理。其实在最早接触环路模型和碎片化模型是在前两个月,那个时候有幸接触企业应用架构方面的知识,里面就有很多业务碎片化的设计技巧。其实理解这些所谓的设计模型后将大大开阔我们的眼界,毕竟研究框架是要研究它的设计原理,它的存在必然是为了解决某一类问题,问题驱动它的设计模型。所以我们在研究这样的模型的时候其实已经在不知不觉的理解问题的本质。

到底环路执行模型是什么?它与碎片化之间是什么关系?假递归式调用又是什么奥秘?这些种种问题我们必须都要把它解决了才能畅通无阻的去研究下面的东西。其实环路执行、碎片化、假递归式都是问题的不同角度的称呼,就好比我们经常会用依赖倒置、控制反转、依赖注入这些词汇去形容设计原则、设计方法一样,他们都是为了解决某种问题而存在,通过巧妙的设计来达到很完美的效果。这里其实也是如此,我们来一一的分解说明。

想必没有人不了解递归的原理,对递归的使用也是很常见的,通过递归算法我们可以解决一下无法解决的大问题,通过将大问题分解成多个同样数据结构的小问题然后让计算机重复的去计算就行了。最为常见的就是遍历树形结构的对象,如:XML,它的每个节点都是一样的对象,所以递归调用的方法也是同一个方法,只不过不断的调用将产生多个调用栈,最后在按照调用顺序的反向出栈就得出一个完整的数据结构。

那么在LINQ中来说,我们无法通过一个方法多次调用来产生我们想要的表达式树,一个Where查询表达式扩展方法可能不仅仅是被LINQ查询表达式所使用,还有可能被ORM的入口方法所使用,比如Update更新的时候就需要Where给出更新的条件,Delete也同样如此。(当然我们这里讨论是LINQ背后的设计原理不单单针对LINQ的技术,而是某一类问题的通用设计模式。)那么我们如何构造出一个类似递归但不是递归的算法结构,方法1可能被方法2调用,方法2也可能被方法1所调用,这样的方法很多,N个方法分别表达不同的语义,具体的构造看使用者的需求,所以这里就出现碎片化的概念了,只有碎片化后才能最大程度的重组,既然能重组了就形成了环路的执行模型。非常完美,看似简单却深不见底的模型我们只了解到冰山一角而已,在企业架构、领域驱动设计方向都已经有着很多成功的案例,要不然也不会被称为设计模式了更为强大的称呼是企业应用架构模式才对。用文字的方式讲解计算机程序问题似乎有点吃力,用代码+图形分析的方式来讲解最适合我们程序员的思维习惯了。下面我用一个简单的例子再附上一些简单的图示来跟大家分享一下这几个模式语言的关系。

大家肯定都知道每逢过年过节都会有很多礼品摆放在超市里商场里买,但是我们都知道一个潜规则,就是这些商品的包装花费了很多功夫,一层套一层,其实里面的东西可能很不起眼,这也是一种营销手段吧。我们暂且不管这里面是什么东西,我们现在要设计一个能够任意进行N层次包装的模型出来,一件商品左一层右一层的反复包装,包装几次我们不管,我们提供能进行N层包装的方法出来就行了。

View Code

/// <summary>
   /// 商品抽象类
   /// </summary>
   public abstract class Merchandise
   {
       /// <summary>
       /// 商品名
       /// </summary>
       public string MerchandiseName { get; protected set; }
       /// <summary>
       /// 单价
       /// </summary>
       public int UnitPrice { get; protected set; }
       /// <summary>
       /// 数量
       /// </summary>
       public int Number { get; protected set; } 

   }
   /// <summary>
   /// 苹果
   /// </summary>
   public class Apple : Merchandise
   {
       public Apple() { }
       private void Init()
       {
           base.MerchandiseName = "进口泰国苹果";
           base.UnitPrice = 8;//8块钱一个
           base.Number = 3;//3个一篮装
       }
   }
   /// <summary>
   /// 香蕉
   /// </summary>
   public class Banana : Merchandise
   {
       public Banana() { }
       private void Init()
       {
           base.MerchandiseName = "云南绿色香蕉";
           base.UnitPrice = 3;//3块钱一根
           base.Number = 10;//10根一篮装
       }
   } 

   /// <summary>
   /// 商品包装类
   /// </summary>
   public static class MerchandisePack
   {
       /// <summary>
       /// 贴一个商标
       /// </summary>
       public static Merchandise PackLogo(this Merchandise mer)
       {
           return mer;
       }
       /// <summary>
       /// 包装一个红色的盒子
       /// </summary>
       public static Merchandise PackRedBox(this Merchandise mer)
       {
           return mer;
       }
       /// <summary>
       /// 包装一个蓝色的盒子
       /// </summary>
       public static Merchandise PackBlueBox(this Merchandise mer)
       {
           return mer;
       }

这么简单的代码我就不一一解释了,这里只是为了演示而用没有添加没用的代码,免得耽误大家时间。

我们看关键部分的代码:

View Code

Apple apple = new Apple();
           apple.PackLogo().PackBlueBox().PackRedBox().PackLogo(); 

           Banana banana = new Banana();
           banana.PackRedBox().PackBlueBox().PackLogo();

这段代码我想完全可以说服我们,碎片化后体现出来的扩展性是多么的灵活。apple在一开始的时候都是需要在上面贴一个小logo的,我们吃苹果的都知道的。由于现在是特殊节日,我们为了给接收礼品的人一点小小的Surprise,所以商家要求商品都统一的套了几层包装,有了这个模型确实方便了很多。

完全实现了独立扩展的能力,不会将包装的方法牵扯到领域对象中去,很干净明了。

(注:查看大图)

通过这种架构模式进行系统开发后,我们一目了然的就知道系统的每一个逻辑的过程,更像一种工作流的执行方式,先是什么然后是什么。不像在IF ELSE里面充斥乱七八糟的逻辑,很难维护。不愧为企业应用架构模式的一种啊。当然LINQ中只有Linq to Object才会出现重复的使用一到两个方法来完成功能,像Linq to Entity 几乎不会出现这种情况。一般针对查询的化只是关键字存在于不同的查询上下文中,这里是为了讲解它的背后设计原理。

2】.N层对象执行模型(纵横向对比链式扩展方法)

其实本来不打算加这一小节的,但是考虑到肯定有部分朋友不是很理解多个对象如何协调的去解决某类问题的。IQueryable<T>接口貌似是一个对象,但是它们都属于一个完整的IQueryable<T>中的一员。N层对象体现在哪里?从一开始的IQueryable被扩展方法所处理就已经开始第一层的对象处理,重复性的环路假递归似的调用就形成N层对象模型。在LINQ中的查询表达式与查询方法其实是一一对应的,扩展方法是纵向的概念,而LINQ查询表达式是横向的,其实两者属于对应关系。详情可以参见本人的“NET深入解析LINQ框架(四:IQueryable、IQueryProvider接口详解)”一文;

3】.LINQ查询表达式和链式查询方法其实都是空壳子

LINQ的真正意图是在方便我们构建表达式树(ExpressionTree),手动构建过表达式树的朋友都会觉得很麻烦(对动态表达式有兴趣的可以参见本人的“.NET深入解析LINQ框架(三:LINQ优雅的前奏)”一文),所以我们可以通过直接编写Lambda的方式调用扩展方法,由于LambdaExpression也是来自于Expression,而Expression<T>又是来自LambdaExpression,别被这些搞晕,Expression<T>其实是简化我们使用表达式的方式。对于Expression<T>的编译方式是编辑器帮我们生成好的,在运行时我们只管获取ExpressionTree就行了。LINQ的查询表达式是通过扩展方法横向支撑的,你不用LINQ也一样可以直接使用各个扩展方法,但是那样会很麻烦,开发速度会很慢,最大的问题不在于此,而是没有统一的查询方式来查询所有的数据源。LINQ的本意和初衷是提供统一的方式来供我们查询所有的数据源,这点很重要。

4】详细的对象结构图(对象的执行原理)

这篇文章的重点就在这一节了,上面说了那么多的话如果朋友能懂还好不懂的话还真是头疼。这一节我将给出LINQ的核心的执行图,我们将很清楚的看见LINQ的最终表达式树的对象结构,它是如何构建一棵完整的树形结构的,IQueryable接口是怎么和IQueryProvider接口配合的,为什么IQueryable具备延迟加载的能力。文章的最后将给出一个完整的Linq to Provider的小例子,喜欢扩展LINQ的朋友肯定会喜欢的。

(注:查看大图)

上图看上去可能会很乱,但是静下心来看还是能理解的,按照DbQueryable生命周期来看,之上而下,如果有问题可以回复评论进一步探讨。

5】.IQueryable<T>与IQueryProvider一对一的关系能否改成一对多的关系

IQueryable对象都有一个配套的IQueryProvider对象,在频繁的创建IQueryable的时候都会重新创建IQueryProvider对象,毕竟是一种浪费。我们可以适当的修改实现IQueryable类的内部结构,让每次创建IQueryable之后能重用上一次的IQueryProvider的对象,毕竟IQueryProvider对象没有任何的中间状态的数据,只是CreateQuery、 Execute两个方法。这里只是本人的一点小小的改进想法,不一定需要考虑这些。

6】.完整的自定义查询

LINQ的分析接近尾声了,这篇文章将是深入分析LINQ的最后一篇。既然已经结束了LINQ的全部分析,那么我们动手写一个小例子,作为想扩展LINQ的小雏形。该例子不会涉及到对表达式树的分析,毕竟表达式树的分析并非易事,后面会有专本的文章来剖析表达式树的完整结构,这里只是全盘的IQueryable和IQueryProvider的实现。

ORM一直是我们比较喜欢去写的框架,这里就使用自定义的IQueryable来查询相应的对象实体。首先我们需要继承IQueryable<T>接口来让LINQ能查询我们自己的数据上下文。

View Code

public class DbQuery<T> : IQueryable<T>, IDisposable
    {
        public DbQuery()
        {
            Provider = new DbQueryProvider();
            Expression = Expression.Constant(this);//最后一个表达式将是第一IQueryable对象的引用。
        }
        public DbQuery(Expression expression)
        {
            Provider = new DbQueryProvider();
            Expression = expression;
        } 

        public Type ElementType
        {
            get { return typeof(T); }
            private set { ElementType = value; }
        } 

        public System.Linq.Expressions.Expression Expression
        {
            get;
            private set;
        } 

        public IQueryProvider Provider
        {
            get;
            private set;
        } 

        public IEnumerator<T> GetEnumerator()
        {
            return (Provider.Execute<T>(Expression) as IEnumerable<T>).GetEnumerator();
        } 

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (Provider.Execute(Expression) as IEnumerable).GetEnumerator();
        } 

        public void Dispose()
        { 

        }
    }

下面需要实现提供程序:

View Code

 public class DbQueryProvider : IQueryProvider
    {
        public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
        {
            return new DbQuery<TElement>();
        }

        public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
        {
            //这里牵扯到对表达式树的分析,就不多说了。
            throw new NotImplementedException();
        }

        public TResult Execute<TResult>(System.Linq.Expressions.Expression expression)
        {
            return default(TResult);//强类型数据集
        }

        public object Execute(System.Linq.Expressions.Expression expression)
        {
            return new List<object>();//弱类型数据集
        }
    }

我们看看如何使用;

View Code

using (DbQuery<Order> dbquery = new DbQuery<Order>())
           {
               var OrderList = from order in dbquery where order.OrderName == "111" select order;
               OrderList.Provider.Execute<List<Order>>(OrderList.Expression);//立即执行
               foreach (var o in OrderList)
               {
                   //延迟执行
               }
           }

喜欢编写ORM框架的朋友完全值得花点时间搞一套自己的LINQ TO ORM,这里只是让你高兴一下。

 

作者:王清培

出处:http://www.cnblogs.com/wangiqngpei557/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

时间: 2024-11-05 17:19:23

.NET深入解析LINQ框架(五:IQueryable、IQueryProvider接口详解)的相关文章

.NET深入解析LINQ框架(四:IQueryable、IQueryProvider接口详解)

阅读目录: 1.开篇介绍 2.扩展Linq to Object (应用框架具有查询功能) 2.1.通过添加IEnumerable<T>对象的扩展方法 2.2.通过继承IEnumerable<T>接口 2.3.详细的对象结构图 3.实现IQueryable<T> .IQueryProvider接口 3.1.延迟加载IEnumertor<T>对象(提高系统性能) 3.2.扩展方法的扩展对象之奥秘(this IQueryable<TSource> so

使用Java构造和解析Json数据的两种方法(详解二)_java

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON数据不须要任何特殊的 API 或工具包. 在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面接着介绍用org.json构造和解析Json数据的方法

Android实现定时器的五种方法实例详解

一.Timer Timer是Android直接启动定时器的类,TimerTask是一个子线程,方便处理一些比较复杂耗时的功能逻辑,经常与handler结合使用. 跟handler自身实现的定时器相比,Timer可以做一些复杂的处理,例如,需要对有大量对象的list进行排序,在TimerTask中执行不会阻塞子线程,常常与handler结合使用,在处理完复杂耗时的操作后,通过handler来更新UI界面. timer.schedule(task, delay,period); task: Time

.NET深入解析LINQ框架(一:LINQ优雅的前奏)

阅读目录: 1.LINQ简述 2.LINQ优雅前奏的音符 2.1.隐式类型 (由编辑器自动根据表达式推断出对象的最终类型) 2.2.对象初始化器 (简化了对象的创建及初始化的过程) 2.3.Lambda表达式 (对匿名方法的改进,加入了委托签名的类型推断并很好的与表达式树的结合) 2.4.扩展方法 (允许在不修改类型的内部代码的情况下为类型添加独立的行为) 2.5.匿名类型 (由对象初始化器推断得出的类型,该类型在编译后自动创建) 2.6.表达式目录树(用数据结构表示程序逻辑代码) 3.LINQ

.NET深入解析LINQ框架(三:LINQ优雅的前奏)

阅读目录: 1.动态LINQ查询(动态构建Expression<T>表达式树) 2.DLR动态语言运行时(基于CLR之上的动态语言运行时) 1].动态LINQ查询(动态构建Expression<T>表达式树) 什么是动态LINQ查询?LINQ的编写是静态的,因为C#是基于静态类型系统原理设计的,在编写时已经确定类型,也就是在编译时就已经知道将要执行什么样的查询,条件是什么.排序方式是什么等等.那么很大一部分应用场合中我们需要根据用户的选择来查询数据源,以往我们都是通过判断的方式来拼

《深入理解Nginx:模块开发与架构解析》一1.5 configure详解

1.5 configure详解 可以看出,configure命令至关重要,下文将详细介绍如何使用configure命令,并分析configure到底是如何工作的,从中我们也可以看出Nginx的一些设计思想. 1.5.1 configure的命令参数 使用help命令可以查看configure包含的参数. ./configure --help 这里不一一列出help的结果,只是把它的参数分为了四大类型,下面将会详述各类型下所有参数的用法和意义. 1. 路径相关的参数 表1-2列出了Nginx在编译

Spring MVC 框架搭建配置方法及详解_JSP编程

现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过要想灵活运用Spring MVC来应对大多数的Web开发,就必须要掌握它的配置及原理. 一.Spring MVC环境搭建:(Spring 2.5.6 + Hibernate 3.2.0) 1. jar包引入 Spring 2.5.6:spring.jar.spring-webmvc.jar.comm

【框架】[Hibernate]构架知识点详解入门与测试实例

转载请注明出处:http://blog.csdn.net/qq_26525215 本文源自[大学之旅_谙忆的博客] Hibernate介绍: Hibernate是一个开放源码的.非常优秀.成熟的O/R Mapping框架.它提供了强大.高性能的Java对象和关系数据的持久化和查询功能. O/R Mapping 对象关系映射(Object Relational Mapping,简称ORM)技术,是通过使用描述对象和数据库之间映射的元数据,将Java程序中的对象自动持久化到关系数据库中. 对象和关系

Android应用框架之应用启动过程详解_Android

在Android的应用框架中,ActivityManagerService是非常重要的一个组件,尽管名字叫做ActivityManagerService,但通过之前的博客介绍,我们知道,四大组件的创建都是有AMS来完成的,其实不仅是应用程序中的组件,连Android应用程序本身也是AMS负责启动的.AMS本身运行在一个独立的进程中,当系统决定要在一个新的进程中启动一个Activity或者Service时就会先启动这个进程.而AMS启动进程的过程是从startProcessLocked启动的. 1