VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • 表达式树——C#测试动态查询构建器

第一部分:C#基础入门
7.表达式树——动态查询构建器
实例介绍
在电商后台、数据分析系统中,动态查询是常见需求:用户可自定义筛选条件(如商品价格区间、类别、销量等),系统需实时生成符合条件的查询语句。硬编码无法覆盖所有可能的条件组合,而表达式树能在运行时动态构建LINQ查询表达式,完美解决这一问题。
本节通过商品动态筛选系vb.net教程C#教程python教程SQL教程access 2010教程
统的实现,带你掌握表达式树的核心用法——从单个条件构建到多条件组合(AND/OR),最终生成可直接用于EF Core或内存集合的查询逻辑。
需求分析
设计一个支持多条件组合的动态查询构建器,核心需求如下:
1.支持常见操作符:等于(==)、大于(>)、小于(<)、包含(Contains)、大于等于(>=)、小于等于(<=);
2.支持多条件组合:AND(&&)、OR(||);
3.支持多数据类型:int(销量)、decimal(价格)、string(类别/名称)、DateTime(创建时间);
4.生成LINQ表达式:输出Expression<Func<T, bool>>,可直接用于Where方法;
5.易用性:提供简洁API,开发者无需直接操作复杂的表达式树节点。
代码实现

  1. 核心模型类
    csharp
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Linq.Expressions;
	
	// 商品模型(覆盖多数据类型)
	public class Product
	{
	public int Id { get; set; }
	public string Name { get; set; } // 字符串类型
	public string Category { get; set; } // 字符串类型
	public decimal Price { get; set; } // decimal类型
	public int Sales { get; set; } // int类型
	public DateTime CreateTime { get; set; } // DateTime类型
	}
  1. 表达式树工具类(核心)
    csharp
	// 参数重绑定器:解决多表达式组合时参数不一致问题
	internal class ParameterRebinder : ExpressionVisitor
	{
	private readonly Dictionary<ParameterExpression, ParameterExpression> _map;
	
	public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
	{
	_map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
	}
	
	public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
	{
	return new ParameterRebinder(map).Visit(exp);
	}
	
	protected override Expression VisitParameter(ParameterExpression node)
	{
	if (_map.TryGetValue(node, out ParameterExpression replacement))
	{
	node = replacement;
	}
	return base.VisitParameter(node);
	}
	}
	
	// 表达式树构建工具类
	public static class ExpressionBuilder
	{
	// 构建"等于"条件(==)
	public static Expression<Func<T, bool>> Equal<T>(string propertyName, object value)
	{
	return BuildBinaryExpression<T>(propertyName, value, Expression.Equal);
	}
	
	// 构建"大于"条件(>)
	public static Expression<Func<T, bool>> GreaterThan<T>(string propertyName, object value)
	{
	return BuildBinaryExpression<T>(propertyName, value, Expression.GreaterThan);
	}
	
	// 构建"小于"条件(<)
	public static Expression<Func<T, bool>> LessThan<T>(string propertyName, object value)
	{
	return BuildBinaryExpression<T>(propertyName, value, Expression.LessThan);
	}
	
	// 构建"包含"条件(Contains,仅适用于字符串)
	public static Expression<Func<T, bool>> Contains<T>(string propertyName, string value)
	{
	var parameter = Expression.Parameter(typeof(T), "x");
	var property = Expression.Property(parameter, propertyName);
	var method = typeof(string).GetMethod("Contains", new[] { typeof(string) })!;
	var constant = Expression.Constant(value, typeof(string));
	var call = Expression.Call(property, method, constant);
	return Expression.Lambda<Func<T, bool>>(call, parameter);
	}
	
	// 组合两个条件(AND)
	public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
	{
	var map = new Dictionary<ParameterExpression, ParameterExpression>
	{
	{ right.Parameters[0], left.Parameters[0] }
	};
	var rightBody = ParameterRebinder.ReplaceParameters(map, right.Body);
	return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left.Body, rightBody), left.Parameters[0]);
	}
	
	// 组合两个条件(OR)
	public static Expression<Func<T, bool>> OrElse<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
	{
	var map = new Dictionary<ParameterExpression, ParameterExpression>
	{
	{ right.Parameters[0], left.Parameters[0] }
	};
	var rightBody = ParameterRebinder.ReplaceParameters(map, right.Body);
	return Expression.Lambda<Func<T, bool>>(Expression.OrElse(left.Body, rightBody), left.Parameters[0]);
	}
	
	// 通用二元表达式构建(Equal/GreaterThan/LessThan共用)
	private static Expression<Func<T, bool>> BuildBinaryExpression<T>(string propertyName, object value, Func<Expression, Expression, BinaryExpression> binaryOperator)
	{
	var parameter = Expression.Parameter(typeof(T), "x"); // 参数:x => ...
	var property = Expression.Property(parameter, propertyName); // 属性访问:x.Price
	var constant = Expression.Constant(Convert.ChangeType(value, property.Type), property.Type); //常量:1000(转换为属性类型)
	var binary = binaryOperator(property, constant); // 二元操作:x.Price > 1000
	return Expression.Lambda<Func<T, bool>>(binary, parameter); // 包装为Lambda:x => x.Price >1000
	}
	}
  1. 动态查询构建器(封装API)
    csharp
	// 动态查询构建器(泛型支持任意类型)
	public class DynamicQueryBuilder<T>
	{
	private Expression<Func<T, bool>> _expression;
	
	// 添加条件(默认AND组合)
	public DynamicQueryBuilder<T> AddCondition(Expression<Func<T, bool>> condition, bool useAnd = true)
	{
	if (_expression == null)
	{
	_expression = condition;
	}
	else
	{
	_expression = useAnd ? _expression.AndAlso(condition) : _expression.OrElse(condition);
	}
	return this;
	}
	
	// 生成最终查询表达式
	public Expression<Func<T, bool>> Build()
	{
	return _expression ?? (x => true); // 默认返回"始终为真"的表达式
	}
	
	// 直接编译为委托(用于内存集合查询)
	public Func<T, bool> Compile()
	{
	return Build().Compile();
	}
	}
  1. 测试程序(商品筛选示例)
    csharp
	class Program
	{
	static void Main(string[] args)
	{
	// 模拟商品数据
	var products = new List<Product>
	{
	new Product { Id=1, Name="笔记本电脑", Category="电子", Price=5999.99m, Sales=1200, CreateTime=new DateTime(2024,1,15) },
	new Product { Id=2, Name="T恤", Category="服装", Price=99.99m, Sales=5000, CreateTime=new DateTime(2024,2,10) },
	new Product { Id=3, Name="智能手机", Category="电子", Price=3999.99m, Sales=3000, CreateTime=new DateTime(2024,3,5) },
	new Product { Id=4, Name="牛奶", Category="食品", Price=59.99m, Sales=10000, CreateTime=new DateTime(2024,4,20) },
	new Product { Id=5, Name="平板电脑", Category="电子", Price=2999.99m, Sales=800, CreateTime=new DateTime(2024,5,1) }
	};
	
	// 动态构建查询条件:类别=电子 AND 价格>2000 AND 销量>1000
	var queryBuilder = new DynamicQueryBuilder<Product>()
	.AddCondition(ExpressionBuilder.Equal<Product>("Category", "电子"))
	.AddCondition(ExpressionBuilder.GreaterThan<Product>("Price", 2000m))
	.AddCondition(ExpressionBuilder.GreaterThan<Product>("Sales", 1000));
	
	// 生成查询并执行
	var query = queryBuilder.Build();
	var result = products.Where(query).ToList();
	
	// 输出结果
	Console.WriteLine("=== 筛选结果(电子类+价格>2000+销量>1000) ===");
	foreach (var p in result)
	{
	Console.WriteLine($"ID:{p.Id} | 名称:{p.Name} | 价格:{p.Price:C} | 销量:{p.Sales}");
	}
	
	// 扩展:添加"名称包含手机"的OR条件
	queryBuilder.AddCondition(ExpressionBuilder.Contains<Product>("Name", "手机"), useAnd: false);
	var extendedResult = products.Where(queryBuilder.Build()).ToList();
	Console.WriteLine("
=== 扩展结果(原条件 OR 名称含手机) ===");
	foreach (var p in extendedResult)
	{
	Console.WriteLine($"ID:{p.Id} | 名称:{p.Name} | 价格:{p.Price:C} | 销量:{p.Sales}");
	}
	}
	}

逐行讲解

  1. ParameterRebinder类
    作用:解决多表达式组合时参数不一致问题(如两个表达式的参数分别是x和y,需统一为x);
    核心方法:VisitParameter——重写ExpressionVisitor的方法,替换参数为目标参数;
  2. ExpressionBuilder工具类
    Equal/GreaterThan/LessThan:通过BuildBinaryExpression通用方法构建二元操作表达式(如x.Price>2000);
    Contains:构建字符串Contains方法调用表达式(如x.Name.Contains("手机")),需反射获取string的Contains方法;
    AndAlso/OrElse:组合两个条件表达式,使用ParameterRebinder统一参数;
  3. DynamicQueryBuilder类
    AddCondition:链式添加条件,支持AND/OR组合;
    Build:生成最终查询表达式,默认返回"始终为真"的表达式(避免空条件);
    Compile:将表达式编译为委托,适用于内存集合查询;
  4. 测试程序
    模拟数据:创建5个商品,覆盖不同类别、价格、销量;
    条件构建:组合"电子类+价格>2000+销量>1000"三个条件;
    执行查询:使用Where方法执行动态生成的表达式,输出结果;
    扩展条件:添加OR条件"名称包含手机",验证组合逻辑正确性;
    基础知识拓展
  5. 表达式树核心概念
    表达式树结构:由节点组成,常见节点类型:
    ParameterExpression:参数节点(如x);
    MemberExpression:成员访问节点(如x.Price);
    ConstantExpression:常量节点(如2000);
    BinaryExpression:二元操作节点(如x.Price>2000);
    MethodCallExpression:方法调用节点(如x.Name.Contains("手机"));
    不可变性:表达式树节点默认不可修改,修改需重新构建;
    编译与执行:通过Compile()方法将表达式树转换为委托,执行委托获取结果;
  6. 表达式树与委托的区别
特性 表达式树(Expression<Func<T,bool>>) 委托(Func<T,bool>)
存储形式 抽象语法树(AST) 编译后的IL代码
运行时操作 可解析、修改、转换为SQL 仅可执行
EF Core支持 支持(转换为SQL) 不支持(内存过滤)
性能 首次编译耗时,后续复用高效 直接执行高效
  1. 表达式树在EF Core中的应用
    EF Core会解析表达式树,生成对应的SQL语句(如WHERE Category='电子' AND Price>2000);
    若使用委托(Func<T,bool>),EF Core会先加载所有数据到内存,再执行委托过滤,性能极低;
  2. 常见应用场景
    动态查询:如电商筛选、报表自定义条件;
    ORM框架:如EF Core、Dapper(部分支持);
    规则引擎:如动态验证规则、工作流条件;
    序列化与反序列化:如将表达式树转换为JSON,跨进程传输;
    总结
    表达式树是C#中动态生成代码的强大工具,核心价值在于运行时构建逻辑。通过本节学习,你掌握了:
    表达式树的基本构建方法(二元操作、方法调用);
    多条件组合(AND/OR)的实现;
    动态查询构建器的封装与使用;
    表达式树在EF Core中的应用要点;
    掌握表达式树,能让你写出更灵活、更高效的动态逻辑,尤其在需要自定义查询或规则的场景中,表达式树是不可或缺的技术。

 本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49429.html


相关教程