转载

ORM开发之解析lambda实现完整查询(附测试例子)

先看一个表达式

query.Where(b => b.Number == 10&&b.Id<20);

表达式结构

一个运算符表示一个表达式,因此,此表达式实际上包含两个子表达式 b.Number==10 和b.Id<20 他们的关系为And

看一个子表达式 b.Number==10按运算符为位置,左边为左操作数,右边为右操作数

以And操作符来看,b.Number==10也为左操作数,b.Id<20为右操作数

再增加其它条件时,也是同样的道理

那么我们解析将一个子表达式 b.Number==10 转换为SQL逻辑,则需要:

  1. 取出左表达式对应的字段名称 Number
  2. 取出运算符 =
  3. 取出右表达式的值 10

表达式类型

由上可以看出,表达式分左边和右边,左右两边也可是子表达式,它们形成一个表达式树,基类型都为 System.Linq.Expressions.Expression

具体类型大致按下面划分为:

  1. BinaryExpression 表示包含二元运算符的表达式。 可以理解为一个子表达式,如 b.Number>10
  2. MemberExpression 表示访问字段或属性。 如 b.Number
  3. NewArrayExpression 表示创建新数组并可能初始化该新数组的元素。
  4. MethodCallExpression 表示对静态方法或实例方法的调用 如 b.Name.Contains("123")
  5. ConstantExpression 表示具有常量值的表达式 如 b.Name="hubro" 
  6. UnaryExpression 表示包含一元运算符的表达式

因此,需要根据不同的类型解析不同的表达式

开始解析

拆分表达式树

/// <summary> /// 拆分表达式树 /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <param name="type"></param> /// <returns></returns> public string BinaryExpressionHandler(Expression left, Expression right, ExpressionType type) {  StringBuilder sb = new StringBuilder();  sb.Append("(");  string needParKey = "=,>,<,>=,<=,<>";  string leftPar = RouteExpressionHandler(left);//获取左边  string typeStr = ExpressionTypeCast(type);//转换运算符  var isRight = needParKey.IndexOf(typeStr) > -1;//用以区分是解析左边的名称还是右边的值  string rightPar = RouteExpressionHandler(right, isRight);//获取右边  string appendLeft = leftPar;  sb.Append(appendLeft);//字段名称  if (rightPar.ToUpper() == "NULL")  {   if (typeStr == "=")    rightPar = " IS NULL ";   else if (typeStr == "<>")    rightPar = " IS NOT NULL ";  }  else  {   sb.Append(typeStr);  }  sb.Append(rightPar);  sb.Append(")");  return sb.ToString(); } 

解析表达式

表达式树也会在这里处理,形成递归调用,当表达式是MemberExpression时,为了区分是左边的属性名还是右边的属性值,加了isRight进行区分

当是MethodCallExpression时,如果是左边,则需要进行解析(这里没有实现),右边只需要执行方法结果即可

/// <summary> /// 解析表达式 /// </summary> /// <param name="exp"></param> /// <param name="isRight"></param> /// <returns></returns> public string RouteExpressionHandler(Expression exp, bool isRight = false) {  if (exp is BinaryExpression)  {   BinaryExpression be = (BinaryExpression)exp;   //重新拆分树,形成递归   return BinaryExpressionHandler(be.Left, be.Right, be.NodeType);  }  else if (exp is MemberExpression)  {   MemberExpression mExp = (MemberExpression)exp;   if (isRight)//按表达式右边值   {    var obj = Expression.Lambda(mExp).Compile().DynamicInvoke();    if (obj is Enum)    {     obj = (int)obj;    }    return obj + "";   }   return mExp.Member.Name;//按左边的名称  }  else if (exp is NewArrayExpression)  {   #region 数组   NewArrayExpression naExp = (NewArrayExpression)exp;   StringBuilder sb = new StringBuilder();   foreach (Expression expression in naExp.Expressions)   {    sb.AppendFormat(",{0}", RouteExpressionHandler(expression));   }   return sb.Length == 0 ? "" : sb.Remove(0, 1).ToString();   #endregion  }  else if (exp is MethodCallExpression)  {   if (isRight)   {    return Expression.Lambda(exp).Compile().DynamicInvoke() + "";   }   //在这里解析方法   throw new Exception("暂不支持");  }  else if (exp is ConstantExpression)  {   #region 常量   ConstantExpression cExp = (ConstantExpression)exp;   if (cExp.Value == null)    return "null";   else   {    return cExp.Value.ToString();   }   #endregion  }  else if (exp is UnaryExpression)  {   UnaryExpression ue = ((UnaryExpression)exp);   return RouteExpressionHandler(ue.Operand, isRight);  }  return null; } 

转换运算符

public string ExpressionTypeCast(ExpressionType expType) {  switch (expType)  {   case ExpressionType.And:    return "&";   case ExpressionType.AndAlso:    return " AND ";   case ExpressionType.Equal:    return "=";   case ExpressionType.GreaterThan:    return ">";   case ExpressionType.GreaterThanOrEqual:    return ">=";   case ExpressionType.LessThan:    return "<";   case ExpressionType.LessThanOrEqual:    return "<=";   case ExpressionType.NotEqual:    return "<>";   case ExpressionType.Or:    return "|";   case ExpressionType.OrElse:    return " OR ";   case ExpressionType.Add:   case ExpressionType.AddChecked:    return "+";   case ExpressionType.Subtract:   case ExpressionType.SubtractChecked:    return "-";   case ExpressionType.Divide:    return "/";   case ExpressionType.Multiply:   case ExpressionType.MultiplyChecked:    return "*";   default:    throw new InvalidCastException("不支持的运算符");  } } 

获取解析值

internal string FormatExpression(Expression<Func<T, bool>> expression) {  string condition;  var visitor = new ExpressionVisitor();  if (expression == null)   return "";  condition = visitor.RouteExpressionHandler(expression.Body);  return condition; } 

拼接完整的SQL

public string GetQuery() {  string where = Condition;  where = string.IsNullOrEmpty(where) ? " 1=1 " : where;  #region group判断  if (groupFields.Count > 0)  {   where += " group by ";   foreach (var item in groupFields)   {    where += item + ",";   }   where = where.Substring(0, where.Length - 1);  }  #endregion  string tableName = typeof(T).Name;  string fileds = string.Join(",", queryFields);  var part = string.Format("select {0} from {1}  where {2}", fileds, tableName, where);  return part; } 

运行输出

var query = new LambdaQuery<Product>();             query.Select(b => new { b.BarCode, b.ProductName, total = b.BarCode.COUNT() });             query.GroupBy(b => new { b.BarCode, b.ProductName });             query.Where(b => b.ProductName == "ddd");             query.Where(b => b.Number == 10);             query.Where(b => b.Number == new aa().bb);//测试获取对象参数             query.OrderBy(b => b.BarCode.COUNT(), true);              Console.Write(query.GetQuery());

ORM开发之解析lambda实现完整查询(附测试例子)

这样,一般查询就能用lambda来表示了,但是一些SQL函数,是没法表示的,和之前说的一样,可以用扩展方法解决

上面上解析方法调用表达式里,解析即可,解析方法比较复杂,就不在这里写了

else if (exp is MethodCallExpression) {  if (isRight)  {   return Expression.Lambda(exp).Compile().DynamicInvoke() + "";  }  //在这里解析方法  throw new Exception("暂不支持"); } 

测试例子下载 http://files.cnblogs.com/files/hubro/LambdaQueryTest2.rar

正文到此结束
Loading...