使用表达式树解析Lambda表达式

在使用Windows Azure Table Storage的API时,发现其查询API极其坑爹,用过C#的人都喜欢了这样的查询方式:

x => x.Name == "test"

但是Windows Azure却没有提供这样的方式,没办法,求人不如求己,自己动手来改造它的API吧。

首先介绍一下什么是表达式树:

表达式树表示树状数据结构的代码,树状结构中的每个节点都是一个表达式,例如一个方法调用或类似 x < y 的二元运算。

其实学过编译原理的话对这个概念应该不陌生,语法处理程序的最终结果是一棵抽象语法树,而表达式树就是类似于抽象语法树的一种结构,不过节点是表达式的元素罢了。

C#中提供了一些类来表示各种表达式,它们的名字都以Expression结尾,例如:BinaryExpression, ConstantExpression等,每个类代表一种表达式类型,这些XXXExpression类都继承于抽象类Expression。

C#提供了另一个类,用于访问表达式树:ExpressionVisitor。这是一个抽象类,有一个核心方法Visit,它接受一个Expression对象,并会根据这个对象的实际表达式类型调用相应的VisitXXX方法。

知道了这些,就可以开始写代码了。

首先,我们需要从ExpressionVisitor类中派生一个类,这里我们把它叫做QueryGenerator:

public class QueryGenerator: ExpressionVisotor
{
}

接下来我们需要覆盖它的一些VisitXXX方法,用来处理特定类型的表达式,例如,我想处理函数调用表达式类型,获取函数调用的结果:

protected override Expression VisitMethodCall(MethodCallExpression node)
{
    LambdaExpression<Func<object>> lambda = Expression.Lambda(Expression.Convert(node, typeof(object)));
    var result = lambda.Compile()();
    return ConstantExpression(result);
}

最后,写一个公开方法,在其中调用Visit方法,开始遍历表达式树:

public ConstantExpression GenerateQuery<T>(LambdaExpression<Predictate<T>> condition)
{
    return Visit(condition.Body);
}

之后,就可以使用

new QueryGenerator<TestModel>(x => x.Name = "Test");

这样的表达式来生成查询字符串了。