Expression映射SQL语句分析

我们今天呢来研究一下从Linq或者Lambda 映射成Sql语句的过程分析,注意我们不能只是注重代码怎么编写实现,要理解他的算法原理,那么我们说呢如果用这个方式去做到我们的自定义ORM还是有一定困难的,只能做一些简单的映射,那这样的话两难取齐一我们还不如用比较成熟的商业ORM,或者直接SQL语句。好了,废话不多说,看代码


  List<Course> courseList = new List<Course>();
            var source = courseList.AsQueryable();
            source.Where(c => c.UnitPrice < 80 && c.CourseId > 10001 && (c.CourseName.Contains("A") || c.CourseLevel.Equals("B")));
  //独立分开写,进行一个转换
            Expression<Func<Course, bool>> lambda =
                c => c.UnitPrice < 80 && c.CourseId > 10001 && (c.CourseName.Contains("A") || c.CourseLevel.Equals("B"));


假如我们有上面的这一段代码,需要执行到数据库那我们是不是要转换成对应的SQL语句呢?对吧,数据库是不认识你上面的表达式的。


那我们又知道 上面的东西肯定会通过表达式目录树来分解。


首先我们要跟踪他的 表达式递归方法,我们来认识一个类  ExpressionVisitor


这个类呢能够帮助我们跟踪Expression的路由。
我们写一个自己的类继承自它把

public class LambdaExpressionToSql : ExpressionVisitor
{
        //声明一个堆栈,用于临时保存数据 记住这个堆栈是先进后出的,后进先出。那么我们递归到最后的最先组合出来
        private Stack<string> conditionStack = null;


         protected override Expression VisitBinary(BinaryExpression node)
        {


            //这里多说两句,这里理解起来是很难得,
            //我们不能想它有多少层的递归,我们只需要知道 从右到坐分解,然后从左到右组合 这是很难的,这里需要多喝六个核桃。



            this.conditionStack.Push(")");
            base.Visit(node.Right);   //递归访问所有的右边(相对的)
                                      //将左右两边的表达式链接-->转换操作符
            this.conditionStack.Push($" {ToStringOperator(node.NodeType)} ");
            base.Visit(node.Left);  //递归访问所有的左边(相对的)
            this.conditionStack.Push("(");


            return node;
        }
         /// <summary>
        /// 访问方法
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            this.Visit(node.Arguments[0]);//右边:方法的参数,返回常量表达式:--》跳转到VisitConstant
            this.Visit(node.Object);//左边:方法所在的对象,返回的是成员表达式 c.CourseName --》跳转到VisitMember

            string leftPart = this.conditionStack.Pop();//获取入栈的数据,为方法解析做参数(移除并返回顶部的对象)  [CourseLevel]
            string rightPart = this.conditionStack.Pop();  //'B'

            //解析方法名称
            switch (node.Method.Name)
            {
                case "Equals":
                    this.conditionStack.Push($"{leftPart}={rightPart}");  //  [CourseLevel]='B'
                    break;
                case "Contains":
                    this.conditionStack.Push($"{leftPart} like '%{rightPart.Replace("'", "")}%'");
                    break;
                case "StartsWith":
                    this.conditionStack.Push($"{leftPart} like '%{rightPart.Replace("'", "")}%'"); ;//将新的解析好的条件字符串压入栈
                    break;
                case "EndsWith":
                    this.conditionStack.Push($"{leftPart} like '% '%{rightPart.Replace("'", "")}%'"); ;
                    break;              
            }
            return node;
        }
        /// <summary>
        /// 访问成员
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitMember(MemberExpression node)
        {
            this.conditionStack.Push($" [{node.Member.Name}] ");//将成员名称入栈(注意要留有空格)
            return node;
        }
        /// <summary>
        /// 访问常量
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitConstant(ConstantExpression node)
        {
            Type type = node.Value.GetType();
            if (type.Equals(typeof(string)) || type.Equals(typeof(DateTime)))//如果是字符串或日期,需要添加单引号
            {
                this.conditionStack.Push($"'{node.Value}'");//将成员值入栈(注意,不要有空格)
            }
            else
            {
                this.conditionStack.Push(node.Value.ToString());
            }
            return node;
        }
        /// <summary>
        /// 获取组合的SQL条件字符串
        /// </summary>
        /// <returns></returns>
        public string GetSqlWhereString()
        {
            string where = string.Concat(this.conditionStack.ToArray());
            this.conditionStack.Clear();
            return where;
        }
        /// <summary>
        /// 转换SQL操作符
        /// </summary>
        /// <param name="etype"></param>
        /// <returns></returns>
        public static string ToStringOperator(ExpressionType etype)
        {
            switch (etype)
            {

                case ExpressionType.GreaterThan: return ">";
                case ExpressionType.LessThan: return "<";
                case (ExpressionType.Equal): return "=";
                case ExpressionType.GreaterThanOrEqual: return ">=";
                case ExpressionType.LessThanOrEqual: return "<=";
                case (ExpressionType.NotEqual): return "<>";
                case (ExpressionType.Not): return "not";
                case (ExpressionType.AndAlso):
                case (ExpressionType.And): return "and";
                case (ExpressionType.OrElse):
                case (ExpressionType.Or): return "or";
                case ExpressionType.Add: return "+";
                case ExpressionType.Subtract: return "-";
                case ExpressionType.Multiply: return "*";
                case ExpressionType.Divide: return "/";
                case ExpressionType.Modulo: return "%";
                //后面的,自己添加...
                default:
                    throw new Exception("没有找到对应的操作符!");
            }
        }

}

posted @ 2020-12-01 10:32  wrjhzou  阅读(475)  评论(1)    收藏  举报