網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
上一篇中說到了 Expression 的一些概念性東西,其實(shí)也是為了這一篇做知識(shí)準(zhǔn)備。為了實(shí)現(xiàn) EFCore 的多條件、連表查詢,簡(jiǎn)化查詢代碼編寫,也就有了這篇文章。
在一些管理后臺(tái)中,對(duì)數(shù)據(jù)進(jìn)行多條件查詢是一件很普遍的事情,比如在用戶列表需要實(shí)現(xiàn)可以對(duì) "用戶名"、"手機(jī)號(hào)"、"賬戶是否凍結(jié)" 等等一系列的條件查詢,常見的處理方式就是通過一系列 if...else... 來對(duì)條件進(jìn)行拼接。這會(huì)導(dǎo)致查詢接口實(shí)現(xiàn)起來堆疊了一堆看起來有用但實(shí)際很繁瑣的代碼。所以根據(jù)前后端請(qǐng)求報(bào)文協(xié)商,我們就可以按照一定的格式來動(dòng)態(tài)創(chuàng)建表達(dá)式樹。
創(chuàng)建 QueryEntity 類
QueryEntity 是前端向 API 傳遞的參數(shù)列表,通過這個(gè)類,服務(wù)端可以知道前端需要查詢哪個(gè)字段,使用什么方法(Equals、Contains)過濾。
/// <summary> /// 查詢實(shí)體 /// </summary> public class QueryEntity { /// <summary> /// 字段名稱 /// </summary> public string Key { get; set; } /// <summary> /// 值 /// </summary> public string Value { get; set; } /// <summary> /// 操作方法,對(duì)應(yīng)OperatorEnum枚舉類 /// </summary> public string Operator { get; set; } /// <summary> /// 邏輯運(yùn)算符,只支持AND、OR /// </summary> public string LogicalOperator { get; set; } }
創(chuàng)建 OperatorEnum 類
OperatorEnum 這是一個(gè)操作方法的枚舉類,規(guī)定了 API 允許的查詢方法,比如 Equals、Contains 等等。
/// <summary> /// 操作方法枚舉 /// </summary> public enum OperatorEnum { /// <summary> /// 等于 /// </summary> Equals, /// <summary> /// 不等于 /// </summary> NotEqual, /// <summary> /// 包含 /// </summary> Contains, /// <summary> /// 由什么開始 /// </summary> StartsWith, /// <summary> /// 由什么結(jié)束 /// </summary> EndsWith, /// <summary> /// 大于 /// </summary> Greater, /// <summary> /// 大于等于 /// </summary> GreaterEqual, /// <summary> /// 小于 /// </summary> Less, /// <summary> /// 小于等于 /// </summary> LessEqual, }
創(chuàng)建 ExpressionExtension 類
ExpressionExtension 類實(shí)現(xiàn)了表達(dá)式樹的動(dòng)態(tài)創(chuàng)建,將前端傳入的多條件查詢轉(zhuǎn)換成表達(dá)式,用于 EFCore 的查詢。
/// <summary> /// 表達(dá)式擴(kuò)展 /// </summary> /// <typeparam name="T">泛型</typeparam> public static class ExpressionExtension<T> where T : class, new() { /// <summary> /// 表達(dá)式動(dòng)態(tài)拼接 /// </summary> public static Expression<Func<T, bool>> ExpressionSplice(List<QueryEntity> entities) { if (entities.Count < 1) { return ex => true; } var expression_first = CreateExpressionDelegate(entities[0]); foreach (var entity in entities.Skip(1)) { var expression = CreateExpressionDelegate(entity); InvocationExpression invocation = Expression.Invoke(expression_first, expression.Parameters.Cast<Expression>()); BinaryExpression binary; // 邏輯運(yùn)算符判斷 if (entity.LogicalOperator.ToUpper().Equals("OR")) { binary = Expression.Or(expression.Body, invocation); } else { binary = Expression.And(expression.Body, invocation); } expression_first = Expression.Lambda<Func<T, bool>>(binary, expression.Parameters); } return expression_first; } /// <summary> /// 創(chuàng)建 Expression<TDelegate> /// </summary> private static Expression<Func<T, bool>> CreateExpressionDelegate(QueryEntity entity) { ParameterExpression param = Expression.Parameter(typeof(T)); Expression key = param; var entityKey = entity.Key.Trim(); // 包含'.',說明是父表的字段 if (entityKey.Contains('.')) { var tableNameAndField = entityKey.Split('.'); key = Expression.Property(key, tableNameAndField[0].ToString()); key = Expression.Property(key, tableNameAndField[1].ToString()); } else { key = Expression.Property(key, entityKey); } Expression value = Expression.Constant(ParseType(entity)); Expression body = CreateExpression(key, value, entity.Operator); var lambda = Expression.Lambda<Func<T, bool>>(body, param); return lambda; } /// <summary> /// 屬性類型轉(zhuǎn)換 /// </summary> /// <param name="entity">查詢實(shí)體</param> /// <returns></returns> private static object ParseType(QueryEntity entity) { try { PropertyInfo property; // 包含'.',說明是子類的字段 if (entity.Key.Contains('.')) { var tableNameAndField = entity.Key.Split('.'); property = typeof(T).GetProperty(tableNameAndField[0], BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); property = property.PropertyType.GetProperty(tableNameAndField[1], BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); } else { property = typeof(T).GetProperty(entity.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); } return Convert.ChangeType(entity.Value, property.PropertyType); } catch (Exception) { throw new ArgumentException("字段類型轉(zhuǎn)換失敗:字段名錯(cuò)誤或值類型不正確"); } } /// <summary> /// 創(chuàng)建 Expression /// </summary> private static Expression CreateExpression(Expression left, Expression value, string entityOperator) { if (!Enum.TryParse(entityOperator, true, out OperatorEnum operatorEnum)) { throw new ArgumentException("操作方法不存在,請(qǐng)檢查operator的值"); } return operatorEnum switch { OperatorEnum.Equals => Expression.Equal(left, Expression.Convert(value, left.Type)), OperatorEnum.NotEqual => Expression.NotEqual(left, Expression.Convert(value, left.Type)), OperatorEnum.Contains => Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), value), OperatorEnum.StartsWith => Expression.Call(left, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), value), OperatorEnum.EndsWith => Expression.Call(left, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), value), OperatorEnum.Greater => Expression.GreaterThan(left, Expression.Convert(value, left.Type)), OperatorEnum.GreaterEqual => Expression.GreaterThanOrEqual(left, Expression.Convert(value, left.Type)), OperatorEnum.Less => Expression.LessThan(left, Expression.Convert(value, left.Type)), OperatorEnum.LessEqual => Expression.LessThanOrEqual(left, Expression.Convert(value, left.Type)), _ => Expression.Equal(left, Expression.Convert(value, left.Type)), }; } }
使用示例
例如有以下兩個(gè)實(shí)體類,Address 是 User 的子類
public class User { public int Id { get; set; } public string Name { get; set; } = string.Empty; public int Age { get; set; } public DateTime CreateTime { get; set; } public Address Address { get; set; } } public class Address { public string Province { get; set; } public string City { get; set; } }
單條件查詢
查詢用戶表中名稱(name) 包含 "chen" :
List<QueryEntity> list = new List<QueryEntity> { new QueryEntity { Key = "name", Value = "chen", Operator = "Contains" } }; var expression = ExpressionExtension<User>.ExpressionSplice(list); // expression = Param_0 => Param_0.Name.Contains("chen")
查詢用戶表中年齡(age) 大于等于 18:
List<QueryEntity> list = new List<QueryEntity> { new QueryEntity { Key = "age", Value = "18", Operator = "GreaterEqual" } }; var expression = ExpressionExtension<User>.ExpressionSplice(list); // expression = Param_0 => Param_0.Name.GreaterThanOrEqual(18)
多條件查詢
查詢用戶表中名稱(name) 包含 "chen" 并且年齡(age) 大于等于 18:
List<QueryEntity> list = new List<QueryEntity> { new QueryEntity { Key = "name", Value = "chen", Operator = "Contains" }, new QueryEntity { Key = "age", Value = "18", Operator = "GreaterEqual", // 注意:這里得填入 "AND",代表兩個(gè)條件是并且的關(guān)系,如果需要查詢名稱包含 "chen" 或者 年齡大于等于18,則填入 "OR" "logicalOperator": "AND" } }; var expression = ExpressionExtension<User>.ExpressionSplice(list); // expression = Param_0 => ((Param_0.Status >= Convert(1, Int32)) And Invoke(Param_1 => Param_1.OpenId.Contains("9JJdFTVt6oimCgdbW61sk"), Param_0))
多表查詢
查詢用戶表中名稱(name) 包含 "chen" 并且 地址(address)在廣東省:
List<QueryEntity> list = new List<QueryEntity> { new QueryEntity { Key = "name", Value = "chen", Operator = "Contains" }, new QueryEntity { Key = "address.Province", Value = "廣東省", Operator = "Equals", // 注意:這里得填入 "AND",代表兩個(gè)條件是并且的關(guān)系,如果需要查詢名稱包含 "chen" 或者 年齡大于等于18,則填入 "OR" "logicalOperator": "AND" } }; var expression = ExpressionExtension<BookingRecord>.ExpressionSplice(list); // expression = {Param_0 => ((Param_0.Address.Province == Convert("廣東省", String)) And Invoke(Param_1 => Param_1.Name.Contains("chen"), Param_0))}
原文鏈接:https://www.cnblogs.com/chenyanbo1024/p/15724055.html
相關(guān)推薦
- 2022-07-25 Android開發(fā)之Fragment懶加載的幾種方式及性能對(duì)比_Android
- 2022-05-18 python必備庫(kù)Matplotlib畫圖神器_python
- 2023-03-13 React?createRef循環(huán)動(dòng)態(tài)賦值ref問題_React
- 2022-03-15 org.springframework.web.client.RestTemplate 上傳文件無(wú)法
- 2022-11-09 React的特征單向數(shù)據(jù)流學(xué)習(xí)_React
- 2022-07-16 SpringMVC @Controller和@RequestMapping注解
- 2022-02-22 Oracle10G序列名因標(biāo)識(shí)符長(zhǎng)度太大導(dǎo)致無(wú)法創(chuàng)建
- 2022-08-18 C/C++?extern和static的使用詳解_C 語(yǔ)言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支