初识表达式树
表达式树:是一种可以描述代码结构的数据结构,它由一个节点组成,节点表示代码中的操作、方法调用或条件表达式等,它将代码中的表达式转换成一个树形结构,每个节点代表了代码中的操作例如,如果你有一个简单的C#表达式x => x.Name == "John",EFCore会将其转化为一个表达式树,这里我们可以通过Expression构建表达式树,如下所示:
Exception<Func<Book, bool>> e1 = b => b.Price > 5;
Exception<Func<Book, double>> e2 = (b1,b2) => b1.Price + b2.Price;
查看表达式树:这里我们可以通过断点调试,鼠标放在e1上面,点击右键点击快速监视,点击DebugView可以看到文本可视化工具, 如下所示:
整个表达式树是一个“或”类型的节点,左节点(Left)是b.Price>5的表达式,右节点(Right)是另外一个表达式,而b.Price>5这个表达式又是一个大于类型的节点 ,当然我们也可以看一下e2,通过快速监视可以看到其节点类型是相加类型,左边是b1右边是b2:
总结:Expression对象存储了运算逻辑,它把运算逻辑保存成抽象语法树(AST),可以在运行时动态获取运算逻辑,而普通委托则没有。
构建表达式树
只有通过代码动态构建表达式树才能更好的发挥表达式树的能力,ParameterExpression、BinaryExpression、MethodCallExpression、ConstantExpression等类几乎都没有提供构造方法,而且所有属性也几乎都是只读的。因此我们一般不会直接创建这些类的实例,而是调用Expression类的Parameter、MakeBinary、Call、Constant等静态方法来生成,这些静态方法我们一般称作创建表达式树的工厂方法,而属性则通过方法参数类设置。
ParameterExpression:表示表达式树中的一个参数,通常用于函数参数或 Lambda 表达式中的参数。
我们不能直接创建 ParameterExpression,而是通过 Expression.Parameter 方法来生成,如下:4
var param = Expression.Parameter(typeof(User), "u");
BinaryExpression:表示两个操作数之间的二元操作,例如加法、减法、比较等。我们可以通过 Expression.MakeBinary 方法来构建。
var left = Expression.Property(param, "Age");
var right = Expression.Constant(18);
var binaryExpression = Expression.MakeBinary(ExpressionType.GreaterThan, left, right);
MethodCallExpression:表示调用方法的表达式。例如,调用 ToString() 方法。我们通过 Expression.Call 来创建它。
var method = Expression.Call(param, typeof(User).GetMethod("ToString"));
ConstantExpression:表示常量的值,通常用于表达式中的常量部分。通过 Expression.Constant 方法来创建。
var constant = Expression.Constant(42);
通过组合这些表达式,我们可以构建一个完整的 Lambda 表达式。例如,构建一个 x => x.Age > 18 的表达式:
var param = Expression.Parameter(typeof(User), "x");
var ageProperty = Expression.Property(param, "Age");
var ageGreaterThan = Expression.Constant(18);
var condition = Expression.GreaterThan(ageProperty, ageGreaterThan);
var lambda = Expression.Lambda<Func<User, bool>>(condition, param);
通过组合多个表达式,我们可以构建更复杂的表达式。例如,使用逻辑操作符(AND、OR)来构建复合条件表达式:
var left = Expression.GreaterThan(Expression.Property(param, "Age"), Expression.Constant(18));
var right = Expression.Equal(Expression.Property(param, "Name"), Expression.Constant("John"));
var combined = Expression.AndAlso(left, right);
var lambda = Expression.Lambda<Func<User, bool>>(combined, param);
总结:通过使用 Expression 类提供的静态工厂方法(如 Expression.Parameter、Expression.MakeBinary、Expression.Call 等),我们可以动态地构建表达式树。这种方式使得我们能够灵活地生成查询条件或操作逻辑,特别是在需要构建动态查询或条件时非常有用。