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("没有找到对应的操作符!");
}
}
}

浙公网安备 33010602011771号