ORM开发之解析lambda实现完整查询(附测试例子)
上次讲解了怎么解析匿名对象(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逻辑,则需要:
- 取出左表达式对应的字段名称 Number
 - 取出运算符 =
 - 取出右表达式的值 10
 
表达式类型
由上可以看出,表达式分左边和右边,左右两边也可是子表达式,它们形成一个表达式树,基类型都为System.Linq.Expressions.Expression
具体类型大致按下面划分为:
- BinaryExpression 表示包含二元运算符的表达式。 可以理解为一个子表达式,如 b.Number>10
 - MemberExpression 表示访问字段或属性。 如 b.Number
 - NewArrayExpression 表示创建新数组并可能初始化该新数组的元素。
 - MethodCallExpression 表示对静态方法或实例方法的调用 如 b.Name.Contains("123")
 - ConstantExpression 表示具有常量值的表达式 如 b.Name="hubro"
 - 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实现完整查询(附测试例子)的更多相关文章
- ORM开发之解析lambda实现group查询(附测试例子)
		
目的:以编程方式实现group查询,在开发ORM时,需要达到这样的效果 先看一个简单的group语句 select BarCode,ProductName,COUNT(BarCode) as tota ...
 - 完爆Facebook/GraphQL,APIJSON全方位对比解析(三)-表关联查询
		
相关阅读: 完爆Facebook/GraphQL,APIJSON全方位对比解析(一)-基础功能 完爆Facebook/GraphQL,APIJSON全方位对比解析(二)-权限控制 自APIJSON发布 ...
 - c++::Mysql::ORM 开发环境搭建
		
官网地址:https://www.codesynthesis.com/products/odb/ 环境搭建:ubuntu16.04-64 1.安装mysqlClient sudo apt-get in ...
 - visio二次开发——图纸解析之线段
		
多写博客,其实还是蛮好的习惯的,当初大学的时候导师就叫我写,但是就是懒,大学的时候,谁不是魔兽或者LOL呢,是吧,哈哈哈. 好了,接着上一篇visio二次开发——图纸解析,我继续写. 摘要: (转发请 ...
 - iOS开发 XML解析和下拉刷新,上拉加载更多
		
iOS开发 XML解析和下拉刷新,上拉加载更多 1.XML格式 <?xml version="1.0" encoding="utf-8" ?> 表示 ...
 - 和S5933比较起来,开发PLX9054比较不幸,可能是第一次开发PCI的缘故吧。因为,很多PCI的例子都是对S5933,就连微软出版的《Programming the Microsoft Windows Driver Model》都提供了一个完整的S5933的例子。 在这篇有关DDK的开发论文里。
		
和S5933比较起来,开发PLX9054比较不幸,可能是第一次开发PCI的缘故吧.因为,很多PCI的例子都是对S5933,就连微软出版的<Programming the Microsoft Wi ...
 - java微信开发API解析(二)-获取消息和回复消息
		
java微信开发API解析(二)-获取消息和回复消息 说明 * 本演示样例依据微信开发文档:http://mp.weixin.qq.com/wiki/home/index.html最新版(4/3/20 ...
 - Spring注解开发-全面解析常用注解使用方法之生命周期
		
本文github位置:https://github.com/WillVi/Spring-Annotation/ 往期文章:Spring注解开发-全面解析常用注解使用方法之组件注册 bean生命周期  ...
 - Python 41 完整查询语句 和 一堆关键字
		
一:完整查询语句 1.拷贝表 *** create table copy_table select *from customer ; 拷贝结构 与数据 create table copy_table ...
 
随机推荐
- Linux Shell 编程中的特殊符号
			
一.井号 # 1.在脚本文件中对一行进行注释. 2.在引号和\符号后不是注释,只是#号本身: echo "12 # hehe" echo '12 # hehe' echo 12 \ ...
 - Cocopod
			
装了好几天,这个是比较全面的,大家可以看看帮助一下 1.新建一个项目,名称:CPTest 2.打开终端,输入"cd"+空格,然后将文件夹拖入到后面 3.回车后继续输入vim Pod ...
 - sql查询重复数据
			
select *from Awhere id in (select id from A group by id having count(1) >= 2) 注释:id 为重复的关键字(更换成所需 ...
 - RDLC报表数据工具栏关闭后打开方法
			
显示方法为:Ctrl + Alt + D 快捷键 只做自己记录用
 - LeetCode题目按公司分类
			
LinkedIn(39) 1 Two Sum 23.0% Easy 21 Merge Two Sorted Lists 35.4% Easy 23 Merge k Sorted Lists 23.3% ...
 - C# 定时器 Timers.Timer  Forms.Timer
			
1.定义在System.Windows.Forms里 Windows.Forms里面的定时器比较简单,只要把工具箱中的Timer控件拖到窗体上,然后设置一下事件和间隔时间等属性就可以了 //启动定时器 ...
 - gdb调试工具vi编译器命令参考网址
			
vi编译器命令:参考http://www.cnblogs.com/junw_china/articles/1708967.html gbd调试命令:参考http://blog.chinaunix.ne ...
 - C#_技巧:真伪随机数
			
使用 Random 产生随机数.(这是一种伪随机数,需要seed,同一个seed后,采用某种算法产生的数字序列都是一样的) 两种写法 错误 for(int i=0;i<100;i++) { ...
 - PYTHON黑帽编程1.5  使用WIRESHARK练习网络协议分析
			
Python黑帽编程1.5 使用Wireshark练习网络协议分析 1.5.0.1 本系列教程说明 本系列教程,采用的大纲母本为<Understanding Network Hacks At ...
 - Key/Value之王Memcached初探:一、掀起Memcached的盖头来
			
一.Memcached是何方神圣? 在数据驱动的Web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的HttpRuntim ...