上次讲解了怎么解析匿名对象(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. 解决 adb.exe 停止工作小续

    继adb 停止工作的问题之后,又碰见了adb 停止工作的问题. 在使用adb install app.apk 之后给出错误信息如下: * daemon not running. starting it ...

  2. unison+inotify

    hostnamectl --static set-hostname tCentos 1.下载文件到/usr/local/srcocamlunisoninotify-tools 2.安装inotify- ...

  3. html、canvas、视频灰度、反色

    效果图: 代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  4. nginx-nginx脚本

    #!/bin/bash #nx Startup script for the Nginx HTTP Server # it is v. version. # chkconfig: - # descri ...

  5. RecylerView完美实现瀑布流效果

    RecylerView包含三种布局管理器,分别是LinearLayoutManager,GridLayoutManager,StaggeredGridLayoutManager,对应实现单行列表,多行 ...

  6. [译] 理解PHP内部函数的定义(给PHP开发者的PHP源码-第二部分)

    文章来自:http://www.hoohack.me/2016/02/10/understanding-phps-internal-function-definitions-ch 原文:https:/ ...

  7. Python yield与实现

    Python yield与实现  yield的功能类似于return,但是不同之处在于它返回的是生成器. 生成器 生成器是通过一个或多个yield表达式构成的函数,每一个生成器都是一个迭代器(但是迭 ...

  8. Module Zero安装

    返回<Module Zero学习目录> 使用模板创建(自动方式) 手动安装 核心(领域)层 基础设施层 展示层 这里需要抱歉的是,这里使用的博客园的Markdown语法,代码显示不是很好看 ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (10) -----第二章 实体数据建模基础之两实体间Is-a和Has-a关系建模、嵌入值映射

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 2-11 两实体间Is-a和Has-a关系建模 问题 你有两张有Is-a和Has-a ...

  10. Java集合类的总结

    Java语言的java.until包中提供了一些集合类,这些集合类又被称为容器.说到集合就会想到数组,集合类与数组的不同之处是,数组的长度是固定的,集合的长度是可变的:数组用来存放基本数据类型,集合从 ...