目录

写在前面

系列文章

表达式树解析

表达式树特性

编译表达树

总结

写在前面

让我们首先简单回顾一下上篇文章介绍的内容,上篇文章介绍了表达式树的基本概念(表达式树又称为“表达式目录树”,以数据形式表示语言级代码,它是一种抽象语法树或者说是一种数据结构),以及两种创建表达式树目录树的方式:以lambda表达式的方式创建,通过API静态方法创建。由于不能将有语句体的lambda表达式转换为表达式树,而有时我们又有这样的需求,那么这种情况你可以选择API的静态方法方式创建,在 .NET Framework 4 中,API 表达式树还支持赋值表达式和控制流表达式,比如循环、条件块和 try-catch 块等。

系列文章

Linq之Lambda表达式初步认识

Linq之Lambda进阶

Linq之隐式类型、自动属性、初始化器、匿名类

Linq之扩展方法

Linq之Expression初见

表达式树解析

我们可以通过API方式创建表达式树,那么我们有没有办法,将给定的表达式树进行解析,分别得到各个部分呢?答案是肯定,下面看一个例子。

有一个这样的表达式树

  //创建表达式树
Expression<Func<int, bool>> expTree = num => num >= ;

可以这样来解析,分别得到各个部分

             //创建表达式树
Expression<Func<int, bool>> expTree = num => num >= ;
//获取输入参数
ParameterExpression param = expTree.Parameters[];
//获取lambda表达式主题部分
BinaryExpression body = (BinaryExpression)expTree.Body;
//获取num>=5的右半部分
ConstantExpression right = (ConstantExpression)body.Right;
//获取num>=5的左半部分
ParameterExpression left = (ParameterExpression)body.Left;
//获取比较运算符
ExpressionType type = body.NodeType;
Console.WriteLine("解析后:{0} {1} {2}",left,type,right);

输出结果

是不是很爽?不知道到这里,你是否对ORM框架中,lambda表达式是如何转化为sql语句有那么一点点的灵感?没有没关系,咱们继续看一个例子。如果数据库中有Person这样的一个数据表。咱们项目中有对应的Person这样的一个持久化类。那么我们创建一个这样的一个查询方法,返回所有龄大于等于18岁的成年人的sql语句。

 namespace Wolfy.ORMDemo
{
class Program
{
static void Main(string[] args)
{
string sql = Query<Person>(person => person.Age >= );
Console.WriteLine(sql);
Console.Read();
}
/// <summary>
/// 得到查询的sql语句
/// </summary>
/// <param name="epression">筛选条件</param>
/// <returns></returns>
static string Query<T>(Expression<Func<T, bool>> epression) where T : class,new()
{
//获取输入参数
ParameterExpression param = epression.Parameters[];
//获取lambda表达式主体部分
BinaryExpression body = (BinaryExpression)epression.Body;
//解析 person.Age
Expression left = body.Left;
string name = (left as MemberExpression).Member.Name;
//获取主体的右部分
ConstantExpression right = (ConstantExpression)body.Right;
//获取运算符
ExpressionType nodeType = body.NodeType;
StringBuilder sb = new StringBuilder();
//使用反射获取实体所有属性,拼接在sql语句中
Type type = typeof(T);
PropertyInfo[] properties = type.GetProperties();
sb.Append("select ");
for (int i = ; i < properties.Length; i++)
{
PropertyInfo property = properties[i];
if (i == properties.Length - )
{
sb.Append(property.Name + " ");
}
else
{
sb.Append(property.Name + " ,");
}
}
sb.Append("from ");
sb.Append(type.Name);
sb.Append(" where ");
sb.Append(name);
if (nodeType == ExpressionType.GreaterThanOrEqual)
{
sb.Append(">=");
}
sb.Append(right);
return sb.ToString();
}
}
class Person
{
public int Age { set; get; }
public string Name { set; get; }
}
}

输出结果

是不是很方便?传进来一个lambda表达式,就可以通过orm框架内部解析,然后转化为sql语句。也就是通过编写lambda就等于写了sql语句,也不用担心不会写sql语句了。

表达式树特性

表达式树应具有永久性。 这意味着如果你想修改某个表达式树,则必须复制该表达式树然后替换其中的节点来创建一个新的表达式树。

那如何修改呢?

可以通过 ExpressionVisitor类遍历现有表达式树,并复制它访问的每个节点。

一个例子

在项目中添加一个AndAlsoModifier 类。

将表达式树中的AndAlse修改为OrElse,代码如下:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Linq.Expressions;
namespace Wolfy.ExpressionModifyDemo
{
/*该类继承 ExpressionVisitor 类,并且专用于修改表示条件 AND 运算的表达式。
* 它将这些运算从条件 AND 更改为条件 OR。
* 为此,该类将重写基类型的 VisitBinary 方法,这是因为条件 AND 表达式表示为二元表达式。
* 在 VisitBinary 方法中,如果传递到该方法的表达式表示条件 AND 运算,
* 代码将构造一个包含条件 OR 运算符(而不是条件 AND 运算符)的新表达式。
* 如果传递到 VisitBinary 的表达式不表示条件 AND 运算,则该方法交由基类实现来处理。
* 基类方法构造类似于传入的表达式树的节点,但这些节点将其子目录树替换为访问器递归生成的表达式树。*/
public class AndAlsoModifier : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitBinary(BinaryExpression node)
{
if (node.NodeType == ExpressionType.AndAlso)
{
Expression left = this.Visit(node.Left);
Expression right = this.Visit(node.Right);
//修改AndAlse为OrElse
return Expression.MakeBinary(ExpressionType.OrElse, left, right, node.IsLiftedToNull, node.Method);
}
return base.VisitBinary(node);
}
}
}

测试代码

 namespace Wolfy.ExpressionModifyDemo
{
class Program
{
static void Main(string[] args)
{
Expression<Func<string, bool>> expr = name => name.Length > && name.StartsWith("G");
//修改前
Console.WriteLine(expr);
AndAlsoModifier treeModifier = new AndAlsoModifier();
Expression modifiedExpr = treeModifier.Modify((Expression)expr);
//修改后
Console.WriteLine(modifiedExpr);
Console.Read();
}
}
}

输出结果

小结:修改表达式树,需继承ExpressionVisitor类,并重写它的VisitBinary(如果是类似AND这类的二元表达式)方法。再举一个例子,如果要将大于修改为小于等于,可修改VisitBinary方法的实现。

         protected override Expression VisitBinary(BinaryExpression node)
{
if (node.NodeType == ExpressionType.GreaterThan)
{
Expression left = this.Visit(node.Left);
Expression right = this.Visit(node.Right);
//修改> 为<=
return Expression.MakeBinary(ExpressionType.LessThanOrEqual, left, right, node.IsLiftedToNull, node.Method);
}
return base.VisitBinary(node);
}

结果

编译表达树

Expression<TDelegate> 类型提供了 Compile 方法以将表达式树表示的代码编译成可执行委托。

还以最上面的那个表达式树为例

  //创建表达式树
Expression<Func<int, bool>> expTree = num => num >= ;

有这样的一个表达式树,现在,我想直接输入一个值,然后得到结果,该如何办呢?可以这样

             //创建表达式树
Expression<Func<int, bool>> expTree = num => num >= ;
// Compile方法将表达式树描述的 lambda 表达式编译为可执行代码,并生成表示该 lambda 表达式的委托。
Func<int, bool> func = expTree.Compile();
//结果
bool result = func();//true
Console.WriteLine(result);

总结

1.通过表达式解析,你可以得到表达式树的各个部分。你会发现如果你写的方法的参数是Expression<Func<t,t>>类型的,你可以更好的使用lambda表达式的特性,操作更方便。例子中,也简单分析了,ORM框架中,是如何将Lambda表达式解析为sql语句的,也希望能激发你的兴趣。

2.表达式树具有永久性的特性,一经创建,如果你想修改某个表达式树,则必须复制该表达式树然后替换其中的节点来创建一个新的表达式树。具体操作可参考上面的例子。

3.通过Complie方法编译后的表达式树,就是一个委托,委托对应的方法的方法体就是表达式树中的lambda表达式,你可以像使用委托一样去使用它。有时你嫌麻烦也可以类似这样直接使用

 bool result = expTree.Compile()();

参考文章

http://msdn.microsoft.com/zh-cn/library/bb397951.aspx

http://msdn.microsoft.com/zh-cn/library/bb546136.aspx

Linq之Expression进阶的更多相关文章

  1. Linq之Expression高级篇(常用表达式类型)

    目录 写在前面 系列文章 变量表达式 常量表达式 条件表达式 赋值表达式 二元运算符表达式 一元运算符表达式 循环表达式 块表达式 总结 写在前面 首先回顾一下上篇文章的内容,上篇文章介绍了表达式树的 ...

  2. Linq之Expression初见

    目录 写在前面 系列文章 Expression 表达式树创建方式 一个例子 总结 写在前面 上篇文章介绍了扩展方法,这篇文章开始将陆续介绍在linq中使用最多的表达式树的相关概念,以概念及例子一一列出 ...

  3. 动态拼接linq 使用Expression构造动态linq语句

    最近在做动态构造linq语句,从网上找了很多,大多数,都是基于一张表中的某一个字段,这样的结果,从网上可以搜到很多.但如果有外键表,需要动态构造外键表中的字段,那么问题来了,学挖掘机哪家强?哦,不是, ...

  4. LINQ的Expression与delegate表达式

    Linq的delegate表达式,Insus.NET觉得它封装得好,让开发时简化了很多代码,而且容易阅读与检索. 比如,我们需要计算优惠给客户金额,打85%折,可以这样写: using System; ...

  5. Linq之Lambda进阶

    目录 写在前面 系列文章 带有标准查询运算符的Lambda Lambda中类型推断 Lambda表达式中变量作用域 异步Lambda 总结 写在前面 上篇文章介绍了Lambda的基本概念以及匿名方法, ...

  6. Linq To Sql进阶系列(六)用object的动态查询与保存log篇

    动态的生成sql语句,根据不同的条件构造不同的where字句,是拼接sql 字符串的好处.而Linq的推出,是为了弥补编程中的 Data != Object 的问题.我们又该如何实现用object的动 ...

  7. .NET深入实战系列—Linq to Sql进阶

    最近在写代码的过程中用到了Linq查询,在查找资料的过程中发现网上的资料千奇百怪,于是自己整理了一些关于Linq中容易让人困惑的地方. 本文全部代码基于:UserInfo与Class两个表,其中Cla ...

  8. linq入门系列导航

    写在前面 为什么突然想起来学学linq呢?还是源于在跟一个同事聊天的时候,说到他们正在弄得一个项目,在里面用到了linq to sql.突然想到距上次使用linq to sql是三年前的事情了.下班回 ...

  9. Linq之Linq to Sql

    目录 写在前面 系列文章 Linq to sql 总结 写在前面 上篇文章介绍了linq to xml的相关内容,linq to xml提供一种更便捷的创建xml树,及查询的途径.这篇文章将继续介绍l ...

随机推荐

  1. WDK编程的一些特殊点

    函数的多线程安全性在内核编程中比用户态应用程序的编程更常见. 调用源 运行环境 原因 driverEntry,DriverUnload 单线程 这两个函数由系统进程的单一线程调用,不会出现多线程同时调 ...

  2. runv containerd 流程分析

    当runv需要启动一个容器的时候,首先需要启动containrd,作为该容器的daemon.因此,启动containerd的相关代码也是从runv/start.go开始.最终,启动containerd ...

  3. u3d 模型ID配置

    换模型为什么要改代码 0.0 你没有逻辑ID->模型的配置么. 做2个配置.. 分别是角色的ID对应模型路径. 然后是里面的动画名对应真实动画名. 比如 ID=1 path = "xx ...

  4. Linux下安装使用NMON监控、分析系统性能

    背景:今天在LoadRunner11.0中使用rstat监控linux过程中,始终提示如下错: Monitor name :UNIX Resources. Cannot initialize the ...

  5. docker中清理冗余的image,container

    1) 首先进入超级用户模式 [root@docker ~]# sudo su2) 删除container ( container运行时是不能删除的 )首先停止container [root@docke ...

  6. linux如何挂载windows下的共享文件

    说明:windows下有一共享文件夹APP,windows本地ip是192.168.9.155现在需要在linux服务器上挂载这个APP文件夹,linux服务器ip是192.168.9.200 操作记 ...

  7. 什么是javascript-SourceMap

    简单说,Source map就是一个信息文件,里面储存着位置信息.也就是说,转换后的代码的每一个位置,所对应的转换前的位置. 有了它,出错的时候,除错工具将直接显示原始代码,而不是转换后的代码.这无疑 ...

  8. poj 3352

    Road Construction Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 11215 Accepted: 5575 De ...

  9. MahApps.Metro怎么调用消息窗口

    网上查看了好多资料,没有找到很清楚明了的结果,经过了多天的研究,无意发现了这个方法来进行全局调用 效果展示:

  10. 作业调度Quartz.NET

    Quartz.NET是一个开源的作业调度框架,是OpenSymphony 的 Quartz API的.NET移植,它用C#写成,可用于winform和asp.net应用中.它提供了巨大的灵活性而不牺牲 ...