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


猜你喜欢
- 这是进行Java Web开发必备的一个过程,仅供新手参考,高手可以忽略!JDK 和 JRE 的区别JRE(Java Runtime Envi
- SchedulingConfigurer实现动态定时,导致ApplicationRunner无效问题描述当通过SchedulingConfi
- 本文介绍了使用C#创建Windows服务的实例代码,分享给大家一、开发环境操作系统:Windows 10 X64开发环境:VS2015编程语
- 引言 在c#中,可能大多数人针对于多线程之间的通讯,是熟能生巧,对于AsyncLocal 和Thre
- 本章节更加具体化的学习编译器还有哪些可以优化的方便,让你的应用展现出更好的性能。JIT编译器版本JIT编译器有不同的版本,而最终你使用哪种,
- 最近要做一个网站,要求实现验证码程序,经过不断调试,终于成功实现功能。一、验证码生成类生成验证码的话需要用到java的Graphics类库,
- 工欲善其事,必先利其器,对于想要深入学习Android源码,必须先掌握Android编译命令.一、引言关于Android Build系统,这
- 简介FTP 简介FTP 即 文件传输协议(英语:File Transfer Protocol 的缩写)是一个用于计算机网络上在客户端和服务器
- 刚开始用的Mouse_up,虽然能捕获事件,但是没有KeyPress事件的Handled属性。发现一个相对简单的方法。1.先让窗体类继承IM
- 本文主要从两个方面对Android Volley框架的使用方法进行讲解,具体内容如下一、网络请求1.get方式请求数据// 1 创建一个请求
- 前言目前正在练手springboot+vue,因为很多步骤会遇到困难,当时查完资料解决,过一段时间就会忘记,所以决定建个系列记录下来。因为中
- 看了Android版QQ的自定义头像功能,决定自己实现,随便熟悉下android绘制和图片处理这一块的知识。先看看效果:思路分析:这个效果可
- 前几天在“Android绘图之渐隐动画”一文中通过画线实现了渐隐动画,但里面有个问题,画笔较粗(大于1)时线段之间会有裂隙,我又改进了一下。
- 本文实例为大家分享了百度人脸识别之人脸识别FaceIdentify,供大家参考,具体内容如下using System.Collections
- 本文实例讲述了Android 开发使用PopupWindow实现弹出警告框的复用类。分享给大家供大家参考,具体如下:Android开发中相信
- OO思想现在已经在软件开发项目中广泛应用,其中最重要的一个特性就是继承,最近偶简单的复习了下在C#中涉及到继承这个特性时,所需要用到的关键字
- 最近在看进程间的通信,看到了fork()函数,虽然以前用过,这次经过思考加深了理解。现总结如下:1.函数本身(1)头文件#include&l
- Android Studio常用快捷键、Android Studio快捷键大全接下来这篇android studio使用教程,主要为大家介绍
- 前言volatile相关的知识其实自己一直都是有掌握的,能大概讲出一些知识,例如:它可以保证可见性;禁止指令重排。这两个特性张口就来,但要再
- 一、MyBatis背景介绍MyBatis是支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码