先看一个表达式
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逻辑,则需要:
由上可以看出,表达式分左边和右边,左右两边也可是子表达式,它们形成一个表达式树,基类型都为 System.Linq.Expressions.Expression
具体类型大致按下面划分为:
因此,需要根据不同的类型解析不同的表达式
/// <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; }
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());
这样,一般查询就能用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