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 表达式实例为例,它的表达式树是这样的:
然后我们可以按照此表达式树结构来构建我们的 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
猜你喜欢
- 如何传入字符串参数,分割并遍历如前台传入字符串参数 String str = "a,b,c,d,e,f";现需
- 本文研究的主要是ConcurrentMap.putIfAbsent(key,value)用法的相关内容,具体如下。业务上经常会遇到有这种场景
- 这篇文章主要介绍了如何基于Java实现对象List排序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- SpringMVC的几个模型对象模型对象的作用主要是保存数据,可以借助它们将数据带到前端。常用的模型对象有以下几个:1.ModelAndVi
- 因为最近用报表导出比较多,所有就提成了一个工具类,本工具类使用的场景为 根据提供的模板来导出Excel报表并且可根据提供的模板S
- 大家好,我是老三,Spring是我们最常用的开源框架,经过多年发展,Spring已经发展成枝繁叶茂的大树,让我们难以窥其全貌。这节,我们回归
- Mybatis mapper模糊查询语句LIKE最近做学校安排的课程设计作业,用到SSM框架,在自己写mapper代码是遇到了模糊查询的问题
- 本文实例为大家分享了Android实现双曲线折线图的具体代码,供大家参考,具体内容如下先看一下效果图1.先下载jar包 mpandroidc
- 概述冒泡排序是一种简单的排序算法。它重复地走访要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行
- 这里简单介绍了一些常用的属性,以及一些术语的解释和举例说明,不太全面,希望读者多多补充。1.重载:函数名相同,参数的个数或参数类型不同; p
- Object是所有类的父类,也就是说java中所有的类都是直接或者间接继承自Object类。比如你随便创建一个classA,虽然没有明说,但
- 最大数给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。注意:输出结果可能非常大,所以你需要返回一个
- 程序目的从java字节码层理解,为何i = i++后,结果是+1之前的数值。而i=++i后,结果是+1之后的值。关键指令iload_<
- 本地仓库是指存在于我们本机的仓库,在我们加入依赖时候,首先会跑到我们的本地仓库去找,如果找不到则会跑到远程仓库中去找。对于依赖的包大家可以从
- 一、重载(Overload)重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有
- 简介我们知道Java中Collection接口下的很多集合都是线程不安全的, 比如 java.util.ArrayList不是线程安全的,
- SpringBoot读取外置logback配置文件springboot项目可以读取外置配置文件,避免了修改配置文件需要重新打包部署的问题。部
- spring Boot源码编译1. git上下拉最新版的spring Boot下载:git clone git@github.com:spr
- 前提最近发现各个频道推荐了很多ULID相关文章,这里对ULID的规范文件进行解读,并且基于Java语言自行实现ULID,通过此实现过程展示U
- 一、简介Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。Spring Cache提供