上次讲解了怎么解析匿名对象(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. 更改机器名后,打开TFS提示工作区错误的处理

    1,打开vs下的"开发人员命令提示"2,按下面格式输入命令:tf workspaces 查看, 假设显示如下: C:\Program Files (x86)\Microsoft V ...

  2. *CF2.D(哥德巴赫猜想)

    D. Taxes time limit per test 2 seconds memory limit per test 256 megabytes input standard input outp ...

  3. 全选或反选表格中第一列的checkbok

    <input type="checkbox" onclick="$('table tr > td:first-child input:checkbox').p ...

  4. C++基础_总结

    (1)多态性都有哪些?(静态和动态,然后分别叙述了一下虚函数和函数重载) 多态分为两种:静态和动态.静态主要包括函数重载和模板:动态主要是依靠虚函数实现的. 静态联编:重载函数不加virtual关键字 ...

  5. js原生代码实现轮播图案例

    一.轮播图是现在网站网页上最常见的效果之一,对于轮播图的功能,要求不同,效果也不同! 我们见过很多通过不同的方式,实现这一效果,但是有很多比较麻烦,而且不容易理解,兼容性也不好. 在这里分享一下,用j ...

  6. Linux内核分析作业7:Linux内核如何装载和启动一个可执行程序

            1.可执行文件的格式 在 Linux 平台下主要有以下三种可执行文件格式: 1.a.out(assembler and link editor output 汇编器和链接编辑器的输出) ...

  7. 初探ReactJS.NET 开发

    ReactJS通常也被称为"React",是一个刚刚在这场游戏中登场的新手.它由Facebook创建,并在2013年首次发布.Facebook认为React在处理SPA问题上可以成 ...

  8. Three.js + HTML5 Audio API 打造3D音乐频谱,Let’s ROCK!

    继续玩味之前写的音乐频谱作品,将原来在Canvas标签上的 作图利用Three.js让它通过WebGL呈现,这样就打造出了一个全立体感的频谱效果了. 项目详情及源码 项目GitHub地址:https: ...

  9. [ASP.NET MVC 小牛之路]12 - Section、Partial View 和 Child Action

    概括的讲,View中的内容可以分为静态和动态两部分.静态内容一般是html元素,而动态内容指的是在应用程序运行的时候动态创建的内容.给View添加动态内容的方式可归纳为下面几种: Inline cod ...

  10. 【requireJS源码学习01】了解整个requireJS的结构

    前言 现在工作中基本离不开requireJS这种模块管理工具了,之前一直在用,但是对其原理不甚熟悉,整两天我们来试着学习其源码,而后在探寻其背后的AMD思想吧 于是今天的目标是熟悉requireJS整 ...