前面我们说到利用表达式树技术实现LINQ-to-SQL,实际上可以针对任何数据源,实现LINQ-to-Everything。这里还涉及到两个重要的接口即IQueryable和IQueryProvider,这些一起为实现通过LINQ访问各种数据源提供了统一的编程接口。

一、认识IQueryable<T>

接口源码如下:

  1. namespace System.Linq
  2. {
  3. //提供对未指定数据类型的特定数据源的查询进行计算的功能。
  4. public interface IQueryable : IEnumerable
  5. {
  6. //获取与 System.Linq.IQueryable 的实例关联的表达式树。
  7. Expression Expression { get; }
  8. //获取在执行与 System.Linq.IQueryable 的此实例关联的表达式树时返回的元素的类型。
  9. Type ElementType { get; }
  10. //获取与此数据源关联的查询提供程序。
  11. IQueryProvider Provider { get; }
  12. }
  13. public interface IQueryable<out T> : IEnumerable<T>, IQueryable, IEnumerable
  14. {
  15. }
  16. }

IQueryable中定义了三个只读的属性,ElementType即为查询对象的类型,Expression即为表达式树,我们的Linq查询表达式都将转换为表达式树,而Provider表示数据源查询提供程序,将Expression翻译为数据源的查询语言,如Sql,并负责最终的数据查询实现。所以我们必须要实现IQueryProvider接口。

二、IQueryProvider

接口源码如下:

  1. namespace System.Linq
  2. {
  3. public interface IQueryProvider
  4. {
  5. IQueryable CreateQuery(Expression expression);
  6. IQueryable<TElement> CreateQuery<TElement>(Expression expression);
  7. object Execute(Expression expression);
  8. TResult Execute<TResult>(Expression expression);
  9. }
  10. }

IQueryProvider接口包含两组方法,CreateQuery顾名思义即是由Provider通过传入的Expression参数创建并返回一个新的IQueryable查询实例。

Execute则是真正解析Expression的,解析即遍历并翻译其为特定查询语言的过程,然后进行查询,并返回查询结果object。

三、定制IQueryProvider

定制查询我们自己的数据源,比如最终实现如下的代码调用方式,该如何实现呢?

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. var provider = new MyQueryProvider();
  6. var queryable = new Query<Person>(provider);
  7.  
  8. var query =
  9. from p in queryable
  10. where p.Age >= &&
  11. p.Gender== “男”
  12. select p;
  13.  
  14. var list = query.ToList();
  15. Console.ReadLine();
  16. }
  17. }

下面先给出这段代码执行的时序图来帮助我们进一步理解整个执行过程:

代码5-6行,对应步骤1-4,实例化了我们自定义的IQueryable;

代码8-12行,对应步骤5-8,在IQueryable实例基础上,进行Linq查询,查询表达式翻译为Expression,构造出新的IQueryable实例;

代码14行,对应步骤9-12,ToList()方法调用,延迟加载执行,查询出结果并返回。

从中可以看出我们的类图大概是这样的:

大体框架就是如此,然后我们需要做的最重要的事情就是实现MyQueryProvider的Execute(),在这个核心方法里解析Expression,翻译为特定数据源的查询语句,并进行查询。

实例代码:

  1. public override object Execute(Expression expression)
  2. {
  3. //遍历表达式树,生成特定数据源的查询语句,例如sql
  4. String myLang = new MyExpressionVisitor().ProcessExpression(expression);
  5. //根据查询语句进行查询得到结果
  6. IEnumerable<Person> results = PersonHelper.DoQuery(myLang);
  7. return results;
  8. }

至于第6行代码,根据查询语句查询出结果,就是各种Helper的职责了,例如SqlHelper。

然后最重要的,还是ProcessExpression(expression)的实现了,换句话说归根结底这个IQueryProvider最核心的功能就是遍历表达式树。

最后给出主要的遍历代码:

  1. public class MyExpressionVisitor
  2. {
  3. private string _myLang;
  4. // 入口方法
  5. public string ProcessExpression(Expression expression)
  6. {
  7. _myLang = string.Empty;
  8. VisitExpression(expression);
  9. return _myLang;
  10. }
  11. private void VisitExpression(Expression expression)
  12. {
  13. switch (expression.NodeType)
  14. {
  15. // 访问 &&
  16. case ExpressionType.AndAlso:
  17. VisitAndAlso((BinaryExpression)expression);
  18. break;
  19. // 访问 等于
  20. case ExpressionType.Equal:
  21. VisitEqual((BinaryExpression)expression);
  22. break;
  23. // 访问 小于和小于等于
  24. case ExpressionType.LessThan:
  25. case ExpressionType.LessThanOrEqual:
  26. VisitLessThanOrEqual((BinaryExpression)expression);
  27. break;
  28. // 访问大于和大于等于
  29. case ExpressionType.GreaterThan:
  30. case ExpressionType.GreaterThanOrEqual:
  31. GreaterThanOrEqual((BinaryExpression)expression);
  32. break;
  33. // 访问调用方法,主要用于解析Contains方法
  34. case ExpressionType.Call:
  35. VisitMethodCall((MethodCallExpression)expression);
  36. break;
  37. // 访问Lambda表达式
  38. case ExpressionType.Lambda:
  39. VisitExpression(((LambdaExpression)expression).Body);
  40. break;
  41. }
  42. }
  43.  
  44. // 访问 &&
  45. private void VisitAndAlso(BinaryExpression andAlso)
  46. {
  47. VisitExpression(andAlso.Left);
  48. VisitExpression(andAlso.Right);
  49. }
  50.  
  51. // 访问 等于
  52. private void VisitEqual(BinaryExpression expression)
  53. {
  54. //...
  55. }
  56.  
  57. // 访问大于等于
  58. private void GreaterThanOrEqual(BinaryExpression expression)
  59. {
  60. //...
  61. }
  62.  
  63. // 访问 小于和小于等于
  64. private void VisitLessThanOrEqual(BinaryExpression expression)
  65. {
  66. //...
  67. }
  68.  
  69. // 访问 方法调用
  70. private void VisitMethodCall(MethodCallExpression expression)
  71. {
  72. //...
  73. }
  74.  
  75. // 获取属性值
  76. private Object GetMemberValue(MemberExpression memberExpression)
  77. {
  78. MemberInfo memberInfo;
  79. Object obj;
  80.  
  81. if (memberExpression == null)
  82. throw new ArgumentNullException("memberExpression");
  83.  
  84. if (memberExpression.Expression is ConstantExpression)
  85. obj = ((ConstantExpression)memberExpression.Expression).Value;
  86. else if (memberExpression.Expression is MemberExpression)
  87. obj = GetMemberValue((MemberExpression)memberExpression.Expression);
  88. else
  89. throw new NotSupportedException("Expression type not supported: "
  90. + memberExpression.Expression.GetType().FullName);
  91.  
  92. memberInfo = memberExpression.Member;
  93. if (memberInfo is PropertyInfo)
  94. {
  95. PropertyInfo property = (PropertyInfo)memberInfo;
  96. return property.GetValue(obj, null);
  97. }
  98. else if (memberInfo is FieldInfo)
  99. {
  100. FieldInfo field = (FieldInfo)memberInfo;
  101. return field.GetValue(obj);
  102. }
  103. else
  104. {
  105. throw new NotSupportedException("MemberInfo type not supported: "
  106. + memberInfo.GetType().FullName);
  107. }
  108. }
  109. }

至此,表达式树系列文章完结,希望对大家有所帮助。

文章参考: The Wayward WebLog.

不可不知的表达式树(3)定制IQueryProvider的更多相关文章

  1. 不可不知的表达式树(1)Expression初探

    说起Lambda表达式,大家基本都很熟悉了,而表达式树(Expression Trees),则属于80%的工作中往往都用不到的那种技术,所以即便不是什么新技术,很多人对其理解都并不透彻.此文意图从表达 ...

  2. 再讲IQueryable<T>,揭开表达式树的神秘面纱

    接上篇<先说IEnumerable,我们每天用的foreach你真的懂它吗?> 最近园子里定制自己的orm那是一个风生水起,感觉不整个自己的orm都不好意思继续混博客园了(开个玩笑).那么 ...

  3. 【转】再讲IQueryable<T>,揭开表达式树的神秘面纱

    [转]再讲IQueryable<T>,揭开表达式树的神秘面纱 接上篇<先说IEnumerable,我们每天用的foreach你真的懂它吗?> 最近园子里定制自己的orm那是一个 ...

  4. LinqToDB 源码分析——处理表达式树

    处理表达式树可以说是所有要实现Linq To SQL的重点,同时他也是难点.笔者看完作者在LinqToDB框架里面对于这一部分的设计之后,心里有一点不知所然.由于很多代码没有文字注解.所以笔者只能接合 ...

  5. LinqToDB 源码分析——生成表达式树

    当我们知道了Linq查询要用到的数据库信息之后.接下就是生成对应的表达式树.在前面的章节里面笔者就已经介绍过.生成表达式树是事实离不开IQueryable<T>接口.而处理表达式树离不开I ...

  6. 说说lambda表达式与表达式树(未完)

    Lambda表达式可以转换成为代码(委托)或者数据(表达式树).若将其赋值给委托,则Lambda表达式将转换为IL代码:如果赋值给 Expression<TDelegate>,则构造出一颗 ...

  7. LinQ实战学习笔记(三) 序列,查询操作符,查询表达式,表达式树

    序列 延迟查询执行 查询操作符 查询表达式 表达式树 (一) 序列 先上一段代码, 这段代码使用扩展方法实现下面的要求: 取进程列表,进行过滤(取大于10M的进程) 列表进行排序(按内存占用) 只保留 ...

  8. C#在泛型类中,通过表达式树构造lambda表达式

    场景 最近对爬虫的数据库架构做调整,需要将数据迁移到MongoDB上去,需要重新实现一个针对MongoDB的Dao泛型类,好吧,动手开工,当实现删除操作的时候问题来了. 我们的删除操作定义如下:voi ...

  9. 转载:C#特性-表达式树

    原文地址:http://www.cnblogs.com/tianfan/ 表达式树基础 刚接触LINQ的人往往觉得表达式树很不容易理解.通过这篇文章我希望大家看到它其实并不像想象中那么难.您只要有普通 ...

随机推荐

  1. Docker 介绍及基础命令

    Docker 简介 Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目.它基于 Google 公司推出的 Go 语言实现. 项目后来加入了 Linu ...

  2. java day02 记录

    一.介绍运算符使用,包含+ - * / 及 自增.三元运算等 package com.itheima_02; /* * 赋值运算符: * 基本的赋值运算符:= * 扩展的赋值运算符:+=,-=,*=, ...

  3. 【莫烦Pytorch】【P1】人工神经网络VS. 生物神经网络

    滴:转载引用请注明哦[握爪] https://www.cnblogs.com/zyrb/p/9700343.html 莫烦教程是一个免费的机器学习(不限于)的学习教程,幽默风俗的语言让我们这些刚刚起步 ...

  4. textCNN原理

    一.TextCnn的结构 1. 嵌入层(embedding layer) textcnn使用预先训练好的词向量作embedding layer.对于数据集里的所有词,因为每个词都可以表征成一个向量,因 ...

  5. docker 基础之监控

    docker容器监控命令 docker ps 命令(查看所有的运行中的容器) docker stats 命令(容器状态监控) [root@bogon ~]# docker stats containe ...

  6. HBase基本概念与基本使用

    1. HBase简介 1.1 什么是HBase HBASE是一个高可靠性.高性能.面向列.可伸缩的分布式存储系统,利用HBASE技术可在廉价PC Server上搭建起大规模结构化存储集群. HBASE ...

  7. JGUI源码:开发中遇到的问题(11)

    1.IE8下浏览器下css body边缘要留一个像素,如果不留的话,很有可能看不到最边缘的像素. 2.同一种颜色在深色背景和浅色背景下给人的感觉不一样,在深色背景下,给人感觉特别亮,所以深色背景下的颜 ...

  8. Ajax简述

    AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术.AJAX = 异步 JavaScript和X ...

  9. 开放源代码的设计层面框架Spring——day03

    spring第三天     一.AOP的相关概念         1.1AOP概述             1.1.1什么是AOP                 AOP:全称是Aspext Orie ...

  10. HTTPS加密那点事-对称、非对称加密、数字证书

    转自:[漫画]https 加密那点事 首先,HTTP协议的缺点:没有对数据进行加密,都是明文传输的.如果要改进这种明文传输的协议,该如何做呢? 对称加密: 在每次发送真实数据之前,服务器先生成一把密钥 ...