上次讲解了怎么解析匿名对象(ORM开发之解析lambda实现group查询),这次来实现解析二元运算,完成基本条件语法

先看一个表达式

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());

  

这样,一般查询就能用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

ORM开发之解析lambda实现完整查询(附测试例子)的更多相关文章

  1. ORM开发之解析lambda实现group查询(附测试例子)

    目的:以编程方式实现group查询,在开发ORM时,需要达到这样的效果 先看一个简单的group语句 select BarCode,ProductName,COUNT(BarCode) as tota ...

  2. 完爆Facebook/GraphQL,APIJSON全方位对比解析(三)-表关联查询

    相关阅读: 完爆Facebook/GraphQL,APIJSON全方位对比解析(一)-基础功能 完爆Facebook/GraphQL,APIJSON全方位对比解析(二)-权限控制 自APIJSON发布 ...

  3. c++::Mysql::ORM 开发环境搭建

    官网地址:https://www.codesynthesis.com/products/odb/ 环境搭建:ubuntu16.04-64 1.安装mysqlClient sudo apt-get in ...

  4. visio二次开发——图纸解析之线段

    多写博客,其实还是蛮好的习惯的,当初大学的时候导师就叫我写,但是就是懒,大学的时候,谁不是魔兽或者LOL呢,是吧,哈哈哈. 好了,接着上一篇visio二次开发——图纸解析,我继续写. 摘要: (转发请 ...

  5. iOS开发 XML解析和下拉刷新,上拉加载更多

    iOS开发 XML解析和下拉刷新,上拉加载更多 1.XML格式 <?xml version="1.0" encoding="utf-8" ?> 表示 ...

  6. 和S5933比较起来,开发PLX9054比较不幸,可能是第一次开发PCI的缘故吧。因为,很多PCI的例子都是对S5933,就连微软出版的《Programming the Microsoft Windows Driver Model》都提供了一个完整的S5933的例子。 在这篇有关DDK的开发论文里。

    和S5933比较起来,开发PLX9054比较不幸,可能是第一次开发PCI的缘故吧.因为,很多PCI的例子都是对S5933,就连微软出版的<Programming the Microsoft Wi ...

  7. java微信开发API解析(二)-获取消息和回复消息

    java微信开发API解析(二)-获取消息和回复消息 说明 * 本演示样例依据微信开发文档:http://mp.weixin.qq.com/wiki/home/index.html最新版(4/3/20 ...

  8. Spring注解开发-全面解析常用注解使用方法之生命周期

    本文github位置:https://github.com/WillVi/Spring-Annotation/ 往期文章:Spring注解开发-全面解析常用注解使用方法之组件注册 bean生命周期 ​ ...

  9. Python 41 完整查询语句 和 一堆关键字

    一:完整查询语句 1.拷贝表 *** create table copy_table select *from customer ; 拷贝结构 与数据 create table copy_table ...

随机推荐

  1. 关于UIAlertAction如何修改sheet上的字体颜色

    相信很多程序员都会遇到需求是这样的: 但是你发现无论怎么设置cancel和Destructive都无法让红色字体移动到下面取消按钮上: 其实之前一直用错,用了ios9之前的UIActionSheet这 ...

  2. CentOS7 编译安装 Git 服务器 Centos 7.0 + Git 2.2.0 + gitosis (实测 笔记)

    环境: 系统硬件:vmware vsphere (CPU:2*4核,内存2G,双网卡) 系统版本:CentOS-7.0-1406-x86_64-DVD.iso 服务器IP:192.168.1.31 域 ...

  3. 启动tomcat 报 Could not delete D:/online/.metadata/.plugins/org.eclipse.wst.server.core/tm

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXkAAADkCAIAAADy7sNDAAAgAElEQVR4nO2deVQUV77H6z+haSeTkZ

  4. 关于全排列 next_permutation() 函数的用法

    这是一个c++函数,包含在头文件<algorithm>里面,下面是基本格式. 1 int a[]; 2 do{ 3 4 }while(next_permutation(a,a+n)); 下 ...

  5. 微软StockTrader应用程序

    这是一个采用 .NET Enterprise Application Server 技术的端到端示例应用程序.应用程序代码可以从 这里 下载. 代码中演示了WCF服务和移动开发,包括用Xamarin ...

  6. ASP.NET MVC 控制器激活(三)

    ASP.NET MVC 控制器激活(三) 前言 在上个篇幅中说到从控制器工厂的GetControllerInstance()方法来执行控制器的注入,本篇要讲是在GetControllerInstanc ...

  7. Step by Step 配置使用HTTPS的ASP.NET Web应用

    原创地址:http://www.cnblogs.com/jfzhu/p/4064654.html 转载请注明出处 有关HTTPS.SSL以及SSL证书的工作原理,参见 <HTTPS那些事(一)H ...

  8. C语言 · 出现次数最多的数

    问题描述 编写一个程序,读入一组整数,这组整数是按照从小到大的顺序排列的,它们的个数N也是由用户输入的,最多不会超过20.然后程序将对这个数组进行统计,把出现次数最多的那个数组元素值打印出来.如果有两 ...

  9. EMC学习之电磁辐射

    我们在接触新鲜事物的时候,通常习惯用自己熟悉的知识去解释自己不熟悉的事物.EMC知识更多的涉及到微波和射频,对于像我这种专注于信号完整性而 对EMC知识知之甚少的菜鸟来说,最初也只能用SI的一些基础知 ...

  10. Tomcat的目录结构

    bin:该目录下存放的是二进制可执行文件,如果是安装版,那么这个目录下会有两个exe文件:tomcat6.exe.tomcat6w.exe,前者是在控制台下启动Tomcat,后者是弹出UGI窗口启动T ...