• 自己动手实现Expression翻译器 – Part I


    伴随.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 LinqToObjectLinqToSql 的核心接口

    123_thumb8

    其中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 = 1000,
        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查询

  • 相关阅读:
    C#网页数据采集(三)HttpWebRequest
    C#获取局域网ip
    C#调用Mail发送QQ邮件
    C#操作Excel(NPOI)
    html文字两行后,就用省略号代替剩下的
    js的dom测试及实例代码
    js循环数组(总结)
    黑马vue---61、为什么vue组件的data要是一个函数
    黑马vue---59-60、组件中的data和methods
    黑马vue---31-32、vue过滤器实例
  • 原文地址:https://www.cnblogs.com/MigCoder/p/3723519.html
Copyright © 2020-2023  润新知