伴随.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
接口上添加扩展方法。 LinqToSql则是在IQueryable
上做扩展。 我们注意到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
,就可以使用一大堆针对IQueryable 的扩展方法啦。 首先是QueryProvider,它只是初步实现了 IQueryProvider 接口,并留下一些方法给子类实现。
////// Linq集成查询的数据提供器 /// public abstract class QueryProvider : IQueryProvider { /// /// 根据表达式创建一个可查询对象 /// public IQueryable CreateQuery(Expression expression) { return (IQueryable)this.Execute(expression); } /// /// 根据表达式创建一个可查询对象 /// public IQueryable CreateQuery (Expression expression) { return new DbQuery (this, expression); } /// /// 根据表达式执行并返回一个结果对象 /// object IQueryProvider.Execute(Expression expression) { return this.Execute(expression); } /// /// 根据表达式执行并返回一个结果对象 /// TResult IQueryProvider.Execute (Expression expression) { return (TResult)this.Execute(expression); } /// /// 执行表达式并返回结果 /// public abstract object Execute(Expression expression); /// /// 翻译表达式为查询语句 /// public abstract string Translate(Expression expression); }
其次是DbQuery
最后是对QueryProvider的具体实现///}/// 一个可使用Lamdba表达式查询的数据库对象 /// public class DbQuery : IQueryable { private readonly QueryProvider _provider; /// /// 创建一个可使用Linq集成查询的对象 /// public DbQuery(QueryProvider provider) { _provider = provider; this.Expression = Expression.Constant(this); } /// /// 创建一个可使用Linq集成查询的对象 /// public DbQuery(QueryProvider provider, Expression expression) : this(provider) { this.Expression = expression; } IEnumerator IEnumerable .GetEnumerator() { return ((IEnumerable )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); } ////// 提供数据库对象的Lamdba表达式查询服务 /// 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(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表达式的类型
////// 数据库表达式类型 /// public enum DbExpressionType { Query = 1000, Select, Column, Table, Join }
这里为什么我要让Query = 1000呢,因为这些DbExpression要跟Expression和谐共处的,算是对Expression的扩展,但是枚举不支持继承,那我就用土一点的方法,从很大的值开始(1000),以后用到就强转咯
////// 列表达式 /// 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 属性 /// /// 值表达式 /// public Expression Value { get; set; } /// /// 归属的查询表达式的别名 /// public string SelectAlias { get; set; } /// /// 列名 /// public string ColumnName { get; set; } /// /// 排序 /// public int Index { get; set; } #endregion } 这里为了让查询类的Expression更具有抽象性,我引入了QueryExpression,让其余DbExpression都继承它。
////// 代表输出查询的表达式(Select、Table、Join等表达式) /// public abstract class QueryExpression : Expression { protected QueryExpression(ExpressionType expressionType, Type type) : base(expressionType, type) { } /// /// 查询的别名 /// public string Alias { get; set; } /// /// 查询的所有列表达式 /// public virtual IEnumerable Columns { get; set; } /// /// 查询的结果类型 /// public Type ElementType { get; set; } /// /// 查询表达式的真正类型 /// public virtual DbExpressionType ExpressionType { get; set; } /// /// 查询的来源 /// public virtual Expression From { get; set; } /// /// 查询表达式的翻译器 /// public object Translator { get; set; } /// /// 扩展(存放翻译器解析表达式时的必要数据) /// public object ExData { get; set; } } ////// 表达式-数据库表 /// public class TableExpression : QueryExpression { /// /// 初始化一个表示数据库表引用的表达式 /// /// 表内元素的类型(对应实体类) /// 表的别名 /// 表的名称 public TableExpression(Type type, string alias, string name) : base((ExpressionType)DbExpressionType.Table, type) { ElementType = type; Alias = alias; Name = name; } /// /// 表的名称 /// public string Name { get; set; } public override DbExpressionType DbExpressionType { get { return DbExpressionType.Table; } } }
////// Select 表达式 /// public class SelectExpression : QueryExpression { public SelectExpression(Type type, string alias, IEnumerable columns, Expression from, Expression where = null, IEnumerablegroupBy = null, IEnumerable 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 属性 /// /// Where条件 /// public Expression Where { get; set; } /// /// GroupBy /// public IEnumerable GroupBy { get; set; } /// /// OrderBy /// public IEnumerable OrderBy { get; set; } public override DbExpressionType DbExpressionType { get { return DbExpressionType.Select; } } #endregion }
////// Join 表达式 /// 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 属性 /// /// 左表 /// public QueryExpression Left { get; set; } /// /// 右表 /// public QueryExpression Right { get; set; } /// /// 左表匹配键 /// public Expression LeftKey { get; set; } /// /// 右表匹配键 /// public Expression RightKey { get; set; } /// /// 左别名 /// public string LeftAlias { get { return Left.Alias; } } /// /// 右别名 /// public string RightAlias { get { return Right.Alias; } } public override DbExpressionType DbExpressionType { get { return DbExpressionType.Join; } } #endregion }
OK,以上就是我们翻译器的基础模型了,下一节讲解如何将Expression与DbExpression互相转换并构建为SQL查询