c# 动态构建LINQ查询表达式

作者:精致码农 • 王亮 时间:2022-03-23 20:40:47 

作者:精致码农

出处:http://cnblogs.com/willick

联系:liam.wang@live.com

最近工作中遇到一个这样的需求:在某个列表查询功能中,可以选择某个数字列(如商品单价、当天销售额、当月销售额等),再选择 小于或等于大于或等于 ,再填写一个待比较的数值,对数据进行查询过滤。

如果只有一两个这样的数字列,那么使用 Entity Framework Core 可以这么写 LINQ 查询:


public Task<List<Product>> GetProductsAsync(string propertyToFilter, MathOperator mathOperator, decimal value)
{
 var query = _context.Products.AsNoTracking();

query = propertyToFilter switch
 {
   "Amount1" when mathOperator == MathOperator.LessThanOrEqual => query.Where(x => x.Amount1 <= value),
   "Amount1" when mathOperator == MathOperator.GreaterThanOrEqual => query.Where(x => x.Amount1 >= value),

"Amount2" when mathOperator == MathOperator.LessThanOrEqual => query.Where(x => x.Amount2 <= value),
   "Amount2" when mathOperator == MathOperator.GreaterThanOrEqual => query.Where(x => x.Amount2 >= value),

_ => throw new ArgumentException($"不支持 {propertyToFilter} 列作为数字列查询", nameof(propertyToFilter))
 };

return query.ToListAsync();
}

如果固定只有一两个数字列且将来也不会再扩展,这样写简单粗暴,也没什么问题。

但如果有几十个数字列,这样使用 swith 模式匹配的写法就太恐怖了,代码大量重复。很自然地,我们得想办法根据属性名动态创建 Where 方法的参数。它的参数类型是:Expression<Func<T, bool>>,是一个表达式参数。

要知道如何动态创建一个类似 Expression<Func<T, bool>> 类型的表达式实例,就要知道如何拆解表达式树。

对于本示例,以 x => x.Amount1 <= value 表达式实例为例,它的表达式树是这样的:

c# 动态构建LINQ查询表达式

然后我们可以按照此表达式树结构来构建我们的 LINQ 表达式:


public Task<List<Product>> GetProductsAsyncV2(string propertyToFilter, MathOperator mathOperator, decimal value)
{
 var query = _context.Products.AsNoTracking();

var paramExp = Expression.Parameter(typeof(Product));
 var memberExp = Expression.PropertyOrField(paramExp, propertyToFilter);
 var valueExp = Expression.Constant(value);
 var compareExp = mathOperator == MathOperator.LessThanOrEqual ?
   Expression.LessThanOrEqual(memberExp, valueExp) :
   Expression.GreaterThanOrEqual(memberExp, valueExp);
 var lambda = Expression.Lambda<Func<Product, bool>>(compareExp, paramExp);

return query.Where(lambda).ToListAsync();
}

每个 Expression.XXX 静态方法返回的都是一个以 Expression 为基类的实例,代表一个表达式。不同的表达式又可以组成一个新的表达式,直到得到我们需要的 Lambda 表达式。这样就形成了一种树形结构,我们称为表达式树。知道如何把一个最终的查询表达式拆解成表达式树,我们就容易动态构建此查询表达式。

得到一个表达式后,我们还可以动态编译并调用该表达式,比如上面示例得到的 lambda 变量,是一个Expression<Func<Product, bool>> 类型,调用其 Compile 方法,可以得到 Func<Product, bool> 类型的委托。


...

var toTestProduct = new Product { Amount1 = 100, Amount2 = 200 };

Func<Product, bool> func = lambda.Compile();
var result = func(toTestProduct);

Console.WriteLine($"The product's {propertyToFilter} is to {mathOperator} {value}.");

// Output: The product's Amount1 is LessThanOrEqual to 150.

你可以通过研究 Expression 类来了解更多动态构建表达式的方法。

动态构建 LINQ 表达式对于不能在编译时建立查询,只能在运行时建立查询的场景很有用。但它的缺点也很明显,不易维护、不易阅读、不易调试。如果最终的表达式执行出错,很难通过调试来发现具体是构建中的那一步写错了,只能凭自己的理解和经验查找错误。所以,如非必须,一般不推荐动态构建 LINQ 查询表达式。

来源:https://www.cnblogs.com/willick/p/14040435.html?utm_source=tuicool&utm_medium=referral

标签:c#,linq,查询表达式
0
投稿

猜你喜欢

  • C# 4.0 大数的运算--BigInteger的应用详解

    2022-02-02 06:40:05
  • 用代码更新你的jar包

    2023-09-19 23:01:04
  • IDEA 2021.3 使用及idea2021.3.1激活使用方法

    2021-06-06 03:49:38
  • springboot使用redis实现从配置到实战

    2023-05-05 09:49:08
  • springboot aop配合反射统一签名验证实践

    2023-08-04 08:14:07
  • spring cloud gateway跨域全局CORS配置方式

    2021-11-02 01:09:00
  • PyQt5内嵌浏览器注入JavaScript脚本实现自动化操作的代码实例

    2023-11-26 15:05:59
  • Spring Aop 如何获取参数名参数值

    2022-09-08 17:00:41
  • 使用springboot跳转到指定页面和(重定向,请求转发的实例)

    2021-10-21 11:09:24
  • 从java中调用matlab详细介绍

    2023-08-01 14:04:17
  • 利用maven引入第三方jar包以及打包

    2023-11-15 04:23:17
  • java原生序列化和Kryo序列化性能实例对比分析

    2023-11-26 16:04:20
  • JAVA 继承基本类、抽象类、接口介绍

    2022-11-11 00:09:40
  • Android Studio和Gradle使用不同位置JDK的问题解决

    2023-06-27 17:35:04
  • java1.8安装及环境变量配置教程

    2023-04-29 10:36:49
  • springboot 动态数据源的实现方法(Mybatis+Druid)

    2021-07-26 04:27:00
  • ImportBeanDefinitionRegistrar手动控制BeanDefinition创建注册详解

    2021-11-11 18:49:18
  • 宝塔面板配置及部署javaweb教程(全网最全)

    2023-11-10 15:26:27
  • 一文搞懂Spring循环依赖的原理

    2023-07-24 19:03:24
  • 聊聊Java的switch为什么不支持long

    2023-08-24 17:35:14
  • asp之家 软件编程 m.aspxhome.com