当前位置:
首页 > temp > 简明python教程 >
-
动态构造任意复杂的 Linq Where 表达式
前言
Linq 是 C# 中一个非常好用的集合处理库,用好了能帮我们简化大量又臭又长的嵌套循环,使处理逻辑清晰可见。EF 查询主要也是依赖 Linq。但是 Linq 相对 sql 也存在一些缺点,最主要的就是动态构造查询的难度。sql 只需要简单进行字符串拼接,操作难度很低(当然出错也相当容易),而 Linq 表达式由于对强类型表达式树的依赖,动态构造查询表达式基本相当于手写 AST(抽象语法树),可以说难度暴增。
AST 已经进入编译原理的领域,对计算机系统的了解程度需求比一般 crud 写业务代码高了几个量级,也导致很多人觉得 EF 不好用,为了写个动态查询要学编译原理这个代价还是挺高的。后来也有一些类似 DynamicLinq 的类库能用表达式字符串写动态查询。
本着学习精神,研究了一段时间,写了一个在我的想象力范围内,可以动态构造任意复杂的 Where 表达式的辅助类。这个辅助类的过滤条件使用了 JqGrid 的高级查询的数据结构,这是我第一个知道能生成复杂嵌套查询,并且查询数据使用 json 方便解析的 js 表格插件。可以无缝根据 JqGrid 的高级查询生成 Where 表达式。
正文
实现
JqGrid 高级查询数据结构定义,用来反序列化:
1 public class JqGridParameter 2 { 3 /// <summary> 4 /// 是否搜索,本来应该是bool,true 5 /// </summary> 6 public string _search { get; set; } 7 /// <summary> 8 /// 请求发送次数,方便服务器处理重复请求 9 /// </summary> 10 public long Nd { get; set; } 11 /// <summary> 12 /// 当页数据条数 13 /// </summary> 14 public int Rows { get; set; } 15 /// <summary> 16 /// 页码 17 /// </summary> 18 public int Page { get; set; } 19 /// <summary> 20 /// 排序列,多列排序时为排序列名+空格+排序方式,多个列之间用逗号隔开。例:id asc,name desc 21 /// </summary> 22 public string Sidx { get; set; } 23 /// <summary> 24 /// 分离后的排序列 25 /// </summary> 26 public string[][] SIdx => Sidx.Split(", ").Select(s => s.Split(" ")).ToArray(); 27 /// <summary> 28 /// 排序方式:asc、desc 29 /// </summary> 30 public string Sord { get; set; } 31 /// <summary> 32 /// 高级搜索条件json 33 /// </summary> 34 public string Filters { get; set; } 35 36 /// <summary> 37 /// 序列化的高级搜索对象 38 /// </summary> 39 public JqGridSearchRuleGroup FilterObject => Filters.IsNullOrWhiteSpace() 40 ? new JqGridSearchRuleGroup { Rules = new[] { new JqGridSearchRule { Op = SearchOper, Data = SearchString, Field = SearchField } } } 41 : JsonSerializer.Deserialize<JqGridSearchRuleGroup>(Filters ?? string.Empty); 42 43 /// <summary> 44 /// 简单搜索字段 45 /// </summary> 46 public string SearchField { get; set; } 47 /// <summary> 48 /// 简单搜索关键字 49 /// </summary> 50 public string SearchString { get; set; } 51 /// <summary> 52 /// 简单搜索操作 53 /// </summary> 54 public string SearchOper { get; set; } 55 56 } 57 58 /// <summary> 59 /// 高级搜索条件组 60 /// </summary> 61 public class JqGridSearchRuleGroup 62 { 63 /// <summary> 64 /// 条件组合方式:and、or 65 /// </summary> 66 public string GroupOp { get; set; } 67 /// <summary> 68 /// 搜索条件集合 69 /// </summary> 70 public JqGridSearchRule[] Rules { get; set; } 71 /// <summary> 72 /// 搜索条件组集合 73 /// </summary> 74 public JqGridSearchRuleGroup[] Groups { get; set; } 75 } 76 77 /// <summary> 78 /// 高级搜索条件 79 /// </summary> 80 public class JqGridSearchRule 81 { 82 /// <summary> 83 /// 搜索字段 84 /// </summary> 85 public string Field { get; set; } 86 /// <summary> 87 /// 搜索字段的大驼峰命名 88 /// </summary> 89 public string PascalField => Field?.Length > 0 ? Field.Substring(0, 1).ToUpper() + Field.Substring(1) : Field; 90 /// <summary> 91 /// 搜索操作 92 /// </summary> 93 public string Op { get; set; } 94 /// <summary> 95 /// 搜索关键字 96 /// </summary> 97 public string Data { get; set; } 98 }
Where 条件生成器,代码有点多,有点复杂。不过注释也很多,稍微耐心点应该不难看懂:
1 /// <summary> 2 /// JqGrid搜索表达式扩展 3 /// </summary> 4 public static class JqGridSearchExtensions 5 { 6 //前端的(不)属于条件搜索需要传递一个json数组的字符串作为参数 7 //为了避免在搜索字符串的时候分隔符是搜索内容的一部分导致搜索关键字出错 8 //无论定义什么分隔符都不能完全避免这种尴尬的情况,所以使用标准的json以绝后患 9 /// <summary> 10 /// 根据搜索条件构造where表达式,支持JqGrid高级搜索 11 /// </summary> 12 /// <typeparam name="T">搜索的对象类型</typeparam> 13 /// <param name="ruleGroup">JqGrid搜索条件组</param> 14 /// <param name="propertyMap">属性映射,把搜索规则的名称映射到属性名称,如果属性是复杂类型,使用点号可以继续访问内部属性</param> 15 /// <returns>where表达式</returns> 16 public static Expression<Func<T, bool>> BuildWhere<T>(JqGridSearchRuleGroup ruleGroup, IDictionary<string, string> propertyMap) 17 { 18 ParameterExpression parameter = Expression.Parameter(typeof(T), "searchObject"); 19 20 return Expression.Lambda<Func<T, bool>>(BuildGroupExpression<T>(ruleGroup, parameter, propertyMap), parameter); 21 } 22 23 /// <summary> 24 /// 构造搜索条件组的表达式(一个组中可能包含若干子条件组) 25 /// </summary> 26 /// <typeparam name="T">搜索的对象类型</typeparam> 27 /// <param name="group">条件组</param> 28 /// <param name="parameter">参数表达式</param> 29 /// <param name="propertyMap">属性映射</param> 30 /// <returns>返回bool的条件组的表达式</returns> 31 private static Expression BuildGroupExpression<T>(JqGridSearchRuleGroup group, ParameterExpression parameter, IDictionary<string, string> propertyMap) 32 { 33 List<Expression> expressions = new List<Expression>(); 34 foreach (var rule in group.Rules ?? new JqGridSearchRule[0]) 35 { 36 expressions.Add(BuildRuleExpression<T>(rule, parameter, propertyMap)); 37 } 38 39 foreach (var subGroup in group.Groups ?? new JqGridSearchRuleGroup[0]) 40 { 41 expressions.Add(BuildGroupExpression<T>(subGroup, parameter, propertyMap)); 42 } 43 44 if (expressions.Count == 0) 45 { 46 throw new InvalidOperationException("构造where子句异常,生成了0个比较条件表达式。"); 47 } 48 49 if (expressions.Count == 1) 50 { 51 return expressions[0]; 52 } 53 54 var expression = expressions[0]; 55 switch (group.GroupOp) 56 { 57 case "AND": 58 foreach (var exp in expressions.Skip(1)) 59 { 60 expression = Expression.AndAlso(expression, exp); 61 } 62 break; 63 case "OR": 64 foreach (var exp in expressions.Skip(1)) 65 { 66 expression = Expression.OrElse(expression, exp); 67 } 68 break; 69 default: 70 throw new InvalidOperationException($"不支持创建{group.GroupOp}类型的逻辑运算表达式"); 71 } 72 73 return expression; 74 } 75 76 private static readonly string[] SpecialRuleOps = {"in", "ni", "nu", "nn"}; 77 78 /// <summary> 79 /// 构造条件表达式 80 /// </summary> 81 /// <typeparam name="T">搜索的对象类型</typeparam> 82 /// <param name="rule">条件</param> 83 /// <param name="parameter">参数</param> 84 /// <param name="propertyMap">属性映射</param> 85 /// <returns>返回bool的条件表达式</returns> 86 private static Expression BuildRuleExpression<T>(JqGridSearchRule rule, ParameterExpression parameter, 87 IDictionary<string, string> propertyMap) 88 { 89 Expression l; 90 91 string[] names = null; 92
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
关于JS定时器的整理
JS中使用Promise.all控制所有的异步请求都完
js中字符串的方法
import-local执行流程与node模块路径解析流程
检测数据类型的四种方法
js中数组的方法,32种方法
前端操作方法
数据类型
window.localStorage.setItem 和 localStorage.setIte
如何完美解决前端数字计算精度丢失与数