伴随.Net3.5到来的Expression,围绕着它产生了各种各样有趣的技术与应用,Linq to object、Linq to sql、Linq to sqllite、Linq to Anything啊~~各种舒爽不侧漏。当然Expression的应用肯定不会狭隘到只能在Linq查询里,只是它本身的性质很适合作为查询表达。不过本系列的目的只是实现自己的Expression翻译器,其他不做探讨。

一. 明确需求

a) 翻译什么(Expression)

b) 翻译成什么(What?)

c) 怎么翻译(How?)

熟悉一门语言(Expression)的时候,想要翻译成别的语言,这个时候翻译成什么就只限制于你掌握的语言数目(Sql?SqlLite?),怎么翻译则取决于你对语言的熟练程度。

既然作为练习,我们就拿比较通用也比较有实际意义的SQL语言来翻译好了。

二.熟悉Expression

这里要求的是你对Expression熟悉,起码能知道它的应用,这里给个学习的链接

Expression Tree上手指南

三.找到翻译入口

3.1 LinqToObject 和 LinqToSql 的核心接口

其中LinqToObject是直接在IEnumerable<T>接口上添加扩展方法。

LinqToSql则是在IQueryable<T>上做扩展。

我们注意到IQueryable有个Expression的成员,这个就是它的查询表达式,比如

//query为一个IQueryable对象
//使用query调用GroupBy扩展方法,返回一个新的IQueryable对象,赋值给result
//那么result.Expression = query.Expression + "x => GroupBy(x.UserName)"
//当然实际不是那么加的,做个比喻而已,总之就是链式查询
var result = query.GroupBy(x => x.UserName);

IQueryProvider像是一个工厂,其两个CreateQuery方法都接收一个Expression然后返回IQueryable对象,我们在代码里遍历IQueryable对象时,其实就是由IQueryable对象将自身的Expression传递给IQueryProvider对象翻译并Excute返回IEnumerator。

OK那么我们的入口很明显了,只要实现自己的IQueryProvider和IQueryable<T>,就可以使用一大堆针对IQueryable<T>的扩展方法啦。

首先是QueryProvider,它只是初步实现了 IQueryProvider 接口,并留下一些方法给子类实现。

/// <summary>
/// Linq集成查询的数据提供器
/// </summary>
public abstract class QueryProvider : IQueryProvider
{
/// <summary>
/// 根据表达式创建一个可查询对象
/// </summary>
public IQueryable CreateQuery(Expression expression)
{
return (IQueryable)this.Execute(expression);
} /// <summary>
/// 根据表达式创建一个可查询对象
/// </summary>
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new DbQuery<TElement>(this, expression);
} /// <summary>
/// 根据表达式执行并返回一个结果对象
/// </summary>
object IQueryProvider.Execute(Expression expression) { return this.Execute(expression); } /// <summary>
/// 根据表达式执行并返回一个结果对象
/// </summary>
TResult IQueryProvider.Execute<TResult>(Expression expression) { return (TResult)this.Execute(expression); } /// <summary>
/// 执行表达式并返回结果
/// </summary>
public abstract object Execute(Expression expression); /// <summary>
/// 翻译表达式为查询语句
/// </summary>
public abstract string Translate(Expression expression);
}

其次是DbQuery

/// <summary>
/// 一个可使用Lamdba表达式查询的数据库对象
/// </summary>
public class DbQuery<T> : IQueryable<T>
{
private readonly QueryProvider _provider; /// <summary>
/// 创建一个可使用Linq集成查询的对象
/// </summary>
public DbQuery(QueryProvider provider)
{
_provider = provider;
this.Expression = Expression.Constant(this);
} /// <summary>
/// 创建一个可使用Linq集成查询的对象
/// </summary>
public DbQuery(QueryProvider provider, Expression expression)
: this(provider)
{
this.Expression = expression;
} IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return ((IEnumerable<T>)Provider.Execute(this.Expression)).GetEnumerator();
} public IEnumerator GetEnumerator()
{
return ((IEnumerable)Provider.Execute(this.Expression)).GetEnumerator();
} public Expression Expression { get; set; } public Type ElementType { get { return typeof(T); } } public IQueryProvider Provider { get { return _provider; } } public override string ToString()
{
return _provider.Translate(this.Expression);
}

}

最后是对QueryProvider的具体实现

/// <summary>
/// 提供数据库对象的Lamdba表达式查询服务
/// </summary>
public class DbQueryProvider : QueryProvider
{
#region 数据库对象 private readonly DbConnection _dbConnection; #endregion public DbQueryProvider(DbConnection conn)
{
_dbConnection = conn;
} public override object Execute(Expression expression)
{
var cmd = _dbConnection.CreateCommand();
cmd.CommandText = this.Translate(expression);
return cmd.ExecuteReader();
} public override string Translate(Expression expression)
{
//先不实现
return string.Empty;
}
}
 

使用示例如下

//初始化一个Provider,并将数据库连接传递
var provider = new DbQueryProvider(conn);
//创建一个Query,将provider传递给它
var query = new DbQuery<User>(provider);
//query.Provider.CreateQuery(query.Where(x => x.UserName == "灰机"))
var result = query.Where(x => x.UserName == "灰机"); //result.Provider.Execute(result.Expresson)
foreach (var user in result)
{
Console.WriteLine(user.UserName);
}

看到这里整个流程应该都比较清晰了。

 

四.基础工作

4.1 既然是将Expression翻译为SQL查询式,那么在我们的项目中就得为SQL语句建模,构建一个DbExpression模块,能够更好的映射SQL表达式结构

首先是SQL表达式的类型

/// <summary>
/// 数据库表达式类型
/// </summary>
public enum DbExpressionType
{
Query = ,
Select,
Column,
Table,
Join
}

这里为什么我要让Query = 1000呢,因为这些DbExpression要跟Expression和谐共处的,算是对Expression的扩展,但是枚举不支持继承,那我就用土一点的方法,从很大的值开始(1000),以后用到就强转咯

/// <summary>
/// 列表达式
/// </summary>
public class ColumnExpression : Expression
{
public ColumnExpression(Type type, Expression value, string selectAlias, string columnName, int index)
: base((ExpressionType)DbExpressionType.Column, type)
{
SelectAlias = selectAlias;
ColumnName = columnName;
Index = index;
Value = value;
} #region 属性 /// <summary>
/// 值表达式
/// </summary>
public Expression Value { get; set; } /// <summary>
/// 归属的查询表达式的别名
/// </summary>
public string SelectAlias { get; set; } /// <summary>
/// 列名
/// </summary>
public string ColumnName { get; set; } /// <summary>
/// 排序
/// </summary>
public int Index { get; set; } #endregion
}

这里为了让查询类的Expression更具有抽象性,我引入了QueryExpression,让其余DbExpression都继承它。

/// <summary>
/// 代表输出查询的表达式(Select、Table、Join等表达式)
/// </summary>
public abstract class QueryExpression : Expression
{
protected QueryExpression(ExpressionType expressionType, Type type)
: base(expressionType, type)
{ }
/// <summary>
/// 查询的别名
/// </summary>
public string Alias { get; set; } /// <summary>
/// 查询的所有列表达式
/// </summary>
public virtual IEnumerable<ColumnExpression> Columns { get; set; } /// <summary>
/// 查询的结果类型
/// </summary>
public Type ElementType { get; set; } /// <summary>
/// 查询表达式的真正类型
/// </summary>
public virtual DbExpressionType ExpressionType { get; set; } /// <summary>
/// 查询的来源
/// </summary>
public virtual Expression From { get; set; } /// <summary>
/// 查询表达式的翻译器
/// </summary>
public object Translator { get; set; } /// <summary>
/// 扩展(存放翻译器解析表达式时的必要数据)
/// </summary>
public object ExData { get; set; }
}
/// <summary>
/// 表达式-数据库表
/// </summary>
public class TableExpression : QueryExpression
{
/// <summary>
/// 初始化一个表示数据库表引用的表达式
/// </summary>
/// <param name="type">表内元素的类型(对应实体类)</param>
/// <param name="alias">表的别名</param>
/// <param name="name">表的名称</param>
public TableExpression(Type type, string alias, string name)
: base((ExpressionType)DbExpressionType.Table, type)
{
ElementType = type;
Alias = alias;
Name = name;
} /// <summary>
/// 表的名称
/// </summary>
public string Name { get; set; } public override DbExpressionType DbExpressionType { get { return DbExpressionType.Table; } }
}
/// <summary>
/// Select 表达式
/// </summary>
public class SelectExpression : QueryExpression
{
public SelectExpression(Type type, string alias, IEnumerable<ColumnExpression> columns, Expression from, Expression where = null,
 IEnumerable<ColumnExpression> groupBy = null, IEnumerable<Expression> orderBy = null, object translator = null)
: base((ExpressionType)DbExpressionType.Select, type)
{
ElementType = type;
Alias = alias;
Columns = columns;
From = from;
Where = where;
GroupBy = groupBy;
OrderBy = orderBy;
Translator = translator;
} #region 属性 /// <summary>
/// Where条件
/// </summary>
public Expression Where { get; set; } /// <summary>
/// GroupBy
/// </summary>
public IEnumerable<ColumnExpression> GroupBy { get; set; } /// <summary>
/// OrderBy
/// </summary>
public IEnumerable<Expression> OrderBy { get; set; } public override DbExpressionType DbExpressionType { get { return DbExpressionType.Select; } } #endregion
}
/// <summary>
/// Join 表达式
/// </summary>
public class JoinExpression : QueryExpression
{
public JoinExpression(Type type, QueryExpression left, QueryExpression right, Expression leftKey, Expression rightKey)
: base((ExpressionType)DbExpressionType.Join, type)
{
Left = left;
Right = right;
LeftKey = leftKey;
RightKey = rightKey;
} #region 属性 /// <summary>
/// 左表
/// </summary>
public QueryExpression Left { get; set; } /// <summary>
/// 右表
/// </summary>
public QueryExpression Right { get; set; } /// <summary>
/// 左表匹配键
/// </summary>
public Expression LeftKey { get; set; } /// <summary>
/// 右表匹配键
/// </summary>
public Expression RightKey { get; set; } /// <summary>
/// 左别名
/// </summary>
public string LeftAlias { get { return Left.Alias; } } /// <summary>
/// 右别名
/// </summary>
public string RightAlias { get { return Right.Alias; } } public override DbExpressionType DbExpressionType { get { return DbExpressionType.Join; } } #endregion
}

OK,以上就是我们翻译器的基础模型了,下一节讲解如何将Expression与DbExpression互相转换并构建为SQL查询

自己动手实现Expression翻译器 – Part I的更多相关文章

  1. 动手实现Expression翻译器1

    动手实现Expression翻译器 – Part I   伴随.Net3.5到来的Expression,围绕着它产生了各种各样有趣的技术与应用,Linq to object.Linq to sql.L ...

  2. 自己动手实现Expression翻译器 – Part Ⅱ

    上一节我们了解了Linq查询大体上是如何运转的,并针对SQL表达式进行建模(DbExpression),这一节的重点在于如何将表达式转换为DbExpression. 可以说只要能生成结构清晰的DbEx ...

  3. 自己动手实现Expression翻译器 – Part Ⅲ

    上一节实现了对TableExpression的解析,通过反射创建实例以及构建该实例的成员访问表达式生成了一个TableExpression,并将其遍历格式化为”Select * From TableN ...

  4. 动手写IL到Lua的翻译器——准备

    文章里的代码粘过来的时候格式有点问题,原因是一开始文章是在订阅号上写的(gamedev101,文末有二维码),不知道为啥贴过来就没了格式,还要手动删行号,就没搞了. 介绍下问题背景: 小说君正在参与的 ...

  5. 自己动手写ORM的感受

    之前看到奋斗前辈和时不我待前辈的自己动手写ORM系列博客,感觉讲解的通俗易懂,清晰透彻.作为一个菜鸟,闲来也想着自己写一个ORM,一来加深自己对 ORM的理解,以求对EF,NHibernate等ROM ...

  6. Windows 8 动手实验系列教程 实验8:Windows应用商店API

    动手实验 实验 8: Windows应用商店API 2012年9月 简介 编写Windows应用商店应用最令人瞩目的理由之一是您可以方便地将它们发布到Windows应用商店.考虑到世界范围内目前有超过 ...

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

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

  8. LeetCode 失败的尝试 10. regular expression matching & 正则

    Regular Expression Matching 看到正则就感觉头大,因为正则用好了就很强大.有挑战的才有意思. 其实没有一点思路.循环的话,不能一一对比,匹配模式解释的是之前的字符.那就先遍历 ...

  9. atitit.自己动手开发编译器and解释器(2) ------语法分析,语义分析,代码生成--attilax总结

    atitit.自己动手开发编译器and解释器(2) ------语法分析,语义分析,代码生成--attilax总结 1. 建立AST 抽象语法树 Abstract Syntax Tree,AST) 1 ...

随机推荐

  1. 有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果(转)

    引用 前几天在网上看到一个淘宝的面试题:有一个很大的整数list,需要求这个list中所有整数的和,写一个可以充分利用多核CPU的代码,来计算结果.一:分析题目 从题中可以看到“很大的List”以及“ ...

  2. PHP Yii框架开发——组织架构网站重构

    最近一段时间在维护公司的组织架构网站(Org),旧版网站只是用了xampp简单搭建了一套环境部署在了windows机器上,代码结构相对简单. 整个架构如下: 整个架构没有用到复杂的结构,class里放 ...

  3. 问题(bug)确实不在代码逻辑上面,往往是配置、权限或者业务逻辑之外的地方(转)

    不能说所有的bug都是纸老虎,但往往那种看似很奇葩的bug,导致的原因确实很简单,烦了你一段时间,找到真相又让你忍不住一笑.什么是奇葩的bug呢.我的定义是:代码逻辑都一样,但在A处是好的,到了B处就 ...

  4. angularjs从零开始(一)

    简介   AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了.所以我做了一些工作(你也可以觉得是小 ...

  5. [ Talk is Cheap Show me the CODE ] : jQuery Mobile页面布

    [ Talk is Cheap Show me the CODE ] : jQuery Mobile页面布局 当我们专注地研究人类生活的空虚,并考虑荣华富贵空幻无常时,或许我们正在阿谀逢迎自己懒惰的天 ...

  6. 如何让虚拟机识别插入的USB闪存驱动器

    首先,打开虚拟机,再就是工具栏 有一台虚拟机 -> 移动设备  -> usb ->欧克. 版权声明:本文博主原创文章,博客,未经同意不得转载.

  7. js中arguments

    arguments 每天一对象,JS天天见,今天我们来看看arguments对象及属性.arguments对象不能显式创建,arguments对象只有函数开始时才可用.函数的 arguments 对象 ...

  8. 关于”机器学习方法“,&quot;深度学习方法&quot;系列

    "机器学习/深度学习方法"系列,我本着开放与共享(open and share)的精神撰写,目的是让很多其它的人了解机器学习的概念,理解其原理,学会应用.如今网上各种技术类文章非常 ...

  9. 【转】Uiautomator Api浅析

    原文地址:http://blog.sina.com.cn/s/blog_ae2575ff01018b2o.html uiautomator api: http://android.toolib.net ...

  10. 【百度地图API】如何制作泡泡放大镜?

    原文:[百度地图API]如何制作泡泡放大镜? 任务描述: 我不喜欢API提供的缩放控件耶…… 我能不能使用其他方式放大地图勒? 当然阔以啦! 现在就来教大家如何动手制作一个可爱的泡泡放大镜! 使用它, ...