目录

写在前面

系列文章

表达式树解析

表达式树特性

编译表达树

总结

写在前面

让我们首先简单回顾一下上篇文章介绍的内容,上篇文章介绍了表达式树的基本概念(表达式树又称为“表达式目录树”,以数据形式表示语言级代码,它是一种抽象语法树或者说是一种数据结构),以及两种创建表达式树目录树的方式:以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. WCF并发控制与实例模式

    WCF实例模式类型与区别 实例化模式 instanceMode percall        单调模式 [ServiceBehavior(InstanceContextMode=InstanceCon ...

  2. 分层图+最短路算法 BZOJ 2763: [JLOI2011]飞行路线

    2763: [JLOI2011]飞行路线 Time Limit: 10 Sec  Memory Limit: 128 MB Description Alice和Bob现在要乘飞机旅行,他们选择了一家相 ...

  3. 简单的抓取淘宝关键字信息、图片的Python爬虫|Python3中级玩家:淘宝天猫商品搜索爬虫自动化工具(第一篇)

    Python3中级玩家:淘宝天猫商品搜索爬虫自动化工具(第一篇) 淘宝改字段,Bugfix,查看https://github.com/hunterhug/taobaoscrapy.git 由于Gith ...

  4. AutoIT 实现Firefox下载

    Firefox下载的完整代码: Func IsVisible($handle) ;WinGetState: 2 = Window is visible If BitAND(WinGetState($h ...

  5. Linux搭建python环境

    环境: CentOS 6.4 前言:CentOS 6.4系统自带的有python2.6.6版本 一.下载文件 python官网:https://www.python.org/downloads/ 版本 ...

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

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

  7. Android中的三种XML解析方式

    在Android中提供了三种解析XML的方式:SAX(Simple API XML),DOM(Document Objrect Model),以及Android推荐的Pull解析方式.下面就对三种解析 ...

  8. Spring addFlashAttribute

    redirectAttributes.addFlashAttribute("result",accountModel); 用这个可以绑定session 但是只能用一次,可以避免最后 ...

  9. App_api设计

    2014年,移动APP的热度丝毫没有减退,并没有像桌面软件被WEB网站那样所取代,不但如此,越来越多的传统应用.网站也都开始制作自己的移动APP,也就是我们常说的IOS客户端.android客户端.这 ...

  10. Android Studio使用中的异常

    Android studio教程:[4]真机测试 1.连不上手机 Android Studio识别不了手机(最后还是恢复成勾中的状态),重启,Android Studio连接真机没反应? 2.连上手机 ...