还在拼冗长的WhereIf吗?100行代码解放这个操作

news/2025/3/17 9:33:48/文章来源:https://www.cnblogs.com/fanshaoO/p/18233291

通常我们在做一些数据过滤的操作的时候,经常需要做一些判断再进行是否要对其进行条件过滤。

普通做法

最原始的做法我们是先通过If()判断是否需要进行数据过滤,然后再对数据源使用Where来过滤数据。
示例如下:

if(!string.IsNullOrWhiteSpace(str))
{query = query.Where(a => a == str);
}

封装WhereIf做法

进阶一些的就把普通做法的代码封装成一个扩展方法,WhereIf指代一个名称,也可以有其他名称,本质是一样的。
示例如下:

public static IQueryable<T> WhereIf<T>([NotNull] this IQueryable<T> query, bool condition, Expression<Func<T, int, bool>> predicate)
{return condition? query.Where(predicate): query;
}

使用方式:

query.WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str);

封装WhereIf做法相比普通做法,已经可以减少我们代码的很多If块了,看起来也优雅一些。
但是如果查询条件增多的话,我们依旧需要写很多WhereIf,就会有这种现象:

query.WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str).WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str).WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str).WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str).WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str).WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str).WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str).WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str).WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str).WhereIf(!string.IsNullOrWhiteSpace(str), a => a == str);

条件一但增多很多的话,这样一来代码看起来就又不够优雅了~

这时候就想,如果只用一个Where传进去一个对象,自动解析条件进行数据过滤,是不是就很棒呢~

WhereObj做法

想法来了,那就动手实现一下。

首先我们需要考虑如何对对象的属性进行标记来获取我们作为条件过滤的对应属性。那就得加一个Attribute,这里实现一个CompareAttribute,用于对对象的属性进行标记。

[AttributeUsage(AttributeTargets.Property)]
public class CompareAttribute : Attribute
{public CompareAttribute(CompareType compareType){CompareType = compareType;}public CompareAttribute(CompareType compareType, string compareProperty) : this(compareType){CompareProperty = compareProperty;}public CompareType CompareType { get; set; }public CompareSite CompareSite { get; set; } = CompareSite.LEFT;public string? CompareProperty { get; set; }
}public enum CompareType
{Equal,NotEqual,GreaterThan,GreaterThanOrEqual,LessThan,LessThanOrEqual,Contains,StartsWith,EndsWith,IsNull,IsNotNull
}public enum CompareSite
{RIGHT,LEFT
}

这里CompareType表示要进行比较的操作,很简单,一目了然。
CompareSite则表示在进行比较的时候比较的数据处于比较符左边还是右边,在CompareAttribute给与默认值在左边,表示比较的源数据处于左边。
CompareProperty则表示比较的属性名称,空的话则直接使用对象名称,如果有值则优先使用。

Attribute搞定了,接下来则实现我们的WhereObj
这里由于需要动态的拼接表达式,这里使用了DynamicExpresso.Core库来进行动态表达式生成。
先上代码:

namespace System.Linq;public static class WhereExtensions
{public static IQueryable<T> WhereObj<T>(this IQueryable<T> queryable, object parameterObject){var interpreter = new Interpreter();interpreter = interpreter.SetVariable("o", parameterObject);var properties = parameterObject.GetType().GetProperties().Where(p => p.CustomAttributes.Any(a=>a.AttributeType == typeof(CompareAttribute)));var whereExpression = new StringBuilder();foreach (var property in properties){if(property.GetValue(parameterObject) == null){continue;}var compareAttribute = property.GetCustomAttribute<CompareAttribute>();var propertyName = compareAttribute!.CompareProperty ?? property.Name;if (typeof(T).GetProperty(propertyName) == null){continue;}if (whereExpression.Length > 0){whereExpression.Append(" && ");}whereExpression.Append(BuildCompareExpression(propertyName, property, compareAttribute.CompareType, compareAttribute.CompareSite));}if(whereExpression.Length > 0){return queryable.Where(interpreter.ParseAsExpression<Func<T, bool>>(whereExpression.ToString(), "q"));}return queryable;}public static IEnumerable<T> WhereObj<T>(this IEnumerable<T> enumerable, object parameterObject){var interpreter = new Interpreter();interpreter = interpreter.SetVariable("o", parameterObject);var properties = parameterObject.GetType().GetProperties().Where(p => p.CustomAttributes.Any(a=>a.AttributeType == typeof(CompareAttribute)));var whereExpression = new StringBuilder();foreach (var property in properties){if(property.GetValue(parameterObject) == null){continue;}var compareAttribute = property.GetCustomAttribute<CompareAttribute>();var propertyName = compareAttribute!.CompareProperty ?? property.Name;if (typeof(T).GetProperty(propertyName) == null){continue;}if (whereExpression.Length > 0){whereExpression.Append(" && ");}whereExpression.Append(BuildCompareExpression(propertyName, property, compareAttribute.CompareType, compareAttribute.CompareSite));}if(whereExpression.Length > 0){return enumerable.Where(interpreter.ParseAsExpression<Func<T, bool>>(whereExpression.ToString(), "q").Compile());}return enumerable;}private static string BuildCompareExpression(string propertyName, PropertyInfo propertyInfo, CompareType compareType, CompareSite compareSite){var source = $"q.{propertyName}";var target = $"o.{propertyInfo.Name}";return compareType switch{CompareType.Equal => compareSite == CompareSite.LEFT ? $"{source} == {target}" : $"{target} == {source}",CompareType.NotEqual => compareSite == CompareSite.LEFT ? $"{source} != {target}" : $"{target} != {source}",CompareType.GreaterThan => compareSite == CompareSite.LEFT ? $"{source} < {target}" : $"{target} > {source}",CompareType.GreaterThanOrEqual => compareSite == CompareSite.LEFT ? $"{source} <= {target}" : $"{target} >= {source}",CompareType.LessThan => compareSite == CompareSite.LEFT ? $"{source} > {target}" : $"{target} < {source}",CompareType.LessThanOrEqual => compareSite == CompareSite.LEFT ? $"{source} >= {target}" : $"{target} <= {source}",CompareType.Contains => compareSite == CompareSite.LEFT ? $"{source}.Contains({target})" : $"{target}.Contains({source})",CompareType.StartsWith => compareSite == CompareSite.LEFT ? $"{source}.StartsWith({target})" : $"{target}.StartsWith({source})",CompareType.EndsWith => compareSite == CompareSite.LEFT ? $"{source}.EndsWith({target})" : $"{target}.EndsWith({source})",CompareType.IsNull => $"{source} == null",CompareType.IsNotNull => $"{source} != null",_ => throw new NotSupportedException()};}
}

代码对IEnumerable和IQueryable都进行了扩展,总共行数100行。
在WhereObj中,我们传入一个parameterObject,然后获取对象的所有加了CompareAttribute的属性。
然后进行循环拼接条件。在循环中我们先判断属性是否有值,有值才会添加表达式。所以建议条件属性都为可空类型。

if(property.GetValue(parameterObject) == null)
{continue;
}

然后获取属性的CompareAttribute, 先指定条件属性名称,在判断属性是否在源对象存在,如果不存在则不处理。

if (typeof(T).GetProperty(propertyName) == null)
{continue;
}

最后就是根据CompareType来动态生成拼接的表达式了。
BuildCompareExpression方法根据CompareType和CompareSite动态拼接表达式字符串,然后使用Interpreter.ParseAsExpression<Func<T, bool>>转换成我们的表达式类型。就完成啦。

测试效果

搞一个Customer类和CustomerFilter,再搞一个数据。

namespace Test
{public class Customer{public string Name { get; set; }public int Age { get; set; }public char Gender { get; set; }}public class CustomerFilter{[Compare(CompareType.StartsWith)]public string? Name { get; set; }[Compare(CompareType.Contains, "Name", CompareSite = CompareSite.RIGHT)]public List<string>? Names { get; set; }[Compare(CompareType.GreaterThan)]public int? Age { get; set; }[Compare(CompareType.Equal)]public char? Gender { get; set; }}public class T{public static IEnumerable<Customer> customers = (new List<Customer> {new Customer() { Name = "David", Age = 31, Gender = 'M' },new Customer() { Name = "Mary", Age = 29, Gender = 'F' },new Customer() { Name = "Jack", Age = 2, Gender = 'M' },new Customer() { Name = "Marta", Age = 1, Gender = 'F' },new Customer() { Name = "Moses", Age = 120, Gender = 'M' },}).AsEnumerable();}}

测试代码

T.customers.WhereObj(new CustomerFilter() 
{//Name = "M",Names = ["Mary", "Jack"],//Age = 20,//Gender = 'M'
}).ToList().ForEach(c => Console.WriteLine(c.Name));


可以看到正常执行。
这样我们在应对条件很多的数据过滤的时候,就可以只用一个WhereObj就可以代替很多个WhereIf的拼接了。同时,在添加新条件的时候我们也无需修改其他业务代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/720410.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

“粘土风格”轻松拿捏,基于函数计算部署 ComfyUI实现AI生图

阿里云函数计算 FC 一键部署火爆全球工作流 AI 生图平台—— ComfyUI ,实现更高质量的图像生成,三步轻松完成“黏土”创意AI画作,晒图赢眼部按摩器等好礼! 活动地址:https://developer.aliyun.com/topic/june/fc?spm=a2c6h.29234099.J_6985717350.4.65997562KE82EH实验准…

医保“视界”大革新:可视化管理系统重塑就医体验

想象一下,在繁忙的医院里,患者和家属不再需要为了查询医保信息而四处奔波,也不再需要为了报销费用而焦头烂额。智慧医保可视化管理系统通过大数据、云计算等先进技术,将医保信息整合到一个平台上,实现了信息的实时更新和可视化展示在数字化浪潮席卷全球的今天,我们的生活…

数据安全之一 加解密介绍

一、场景信息系统不断发展,要求对于其中的数据安全性必须增加保护机制,否则数据泄露将会给企业带来不可估量的损失。基于此,数据的安全包括如下: 1)数据存储的安全存储包括:文件存储(包括含有敏感信息的图片)、数据库中的敏感数据的存储(比如:密码)、配置文件中的敏…

stm32系列--同一定时器双通道捕获

void TIM3_Cap_Init(u16 arr,u16 psc)//定时器3通道2输入捕获配置 {TIM_ICInitTypeDef TIM_ICInitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Pe…

赛博斗地主——使用大语言模型扮演Agent智能体玩牌类游戏。

通过大模型来实现多个智能体进行游戏对局这个想对已经比较成熟了无论是去年惊艳的斯坦福小镇还是比如metaGPT或者类似的框架都是使用智能体技术让大模型来操控,从而让大模型跳出自身“预测下一个token”的文字功能去探索更多的应用落地可能性。不过一直没有真正操作过,直到前…

Ollama,在centos7宿主机上,curl http://宿主机IP:11434 提示拒绝访问 ,但是curl http://localhost:11434 正常

Ollama,在centos7宿主机上,curl http://宿主机IP:11434 提示拒绝访问 ,但是curl http://localhost:11434 正常1.使用 netstat 或 ss 命令在宿主机上检查 11434 端口的状态[root@localhost ~]# netstat -tulnp | grep 11434 tcp 0 0 127.0.0.1:11434 0.…

NFS,smb和数据库文件

nfs的搭建网上有很多,可自行查看 Windows Server2012 R2搭建NFS服务器 - 知乎 (zhihu.com) 其中Windows10家庭版不支持NFS客户端,目前Windows上的协议是V3版本,防火墙上有NFS的选项,端口2049udp和tcp 在客户端上的访问和smb一样,都可映射网络驱动器,使用\\ip\目录方式访问…

JavaDoc生成文档

javaDoc命令是用来生成自己API文档的参数信息:@auchor:作者名 @version:版本号 @since:指明需要最早使用的jdk版本 @param:参数名 @return:返回值情况 @throws:异常抛出情况//主要生成的参数 如:/*** @author 林伟填* @version 1.0* @since 1.8*/public class doc {Str…

心诺安 x TapData:快速搭建云中数仓,助力电商企业实施“以用户为中心的”精细化运营

电商企业如何充分整合并利用自身数据资源,实现高效的数据管理和业务流程优化,迎接用户导向的电商精细化运营时代的挑战。使用 TapData,化繁为简,摆脱手动搭建、维护数据管道的诸多烦扰,轻量代替 OGG、DSG 等同步工具,「CDC + 流处理 + 数据集成」组合拳,加速仓内数据流转…

Docker安装使用教程

Docker安装使用教程Docker是什么Docker是一个容器化开源平台,它使开发者可以再容器中封装应用程序,以及其依赖的所有组件,包括操作系统、库文件、环境变量等,并以轻量级、可移植的方式进行交付和部署.Docker的三大核心概念是:镜像(Image):镜像是Docker的基本构建块,它是一…

未来5年,只有这种产品团队才能开启上帝视角【玩转IPD】

一家企业如何在波涛汹涌的市场浪潮中站稳脚跟?一个团队如何快速识别风险发现机遇,成为行业的标杆?一家企业如何在波涛汹涌的市场浪潮中站稳脚跟?一个团队如何快速识别风险发现机遇,成为行业的标杆?市场瞬息万变,如何准确地响应市场动向,紧跟用户需求?这些问题,已成为…

Linux 部署 MinIO(远程服务器)

1. 下载安装 进入 Linux 内# 我习惯放在local下 cd /usr/local/# 新建目录 mkdir minio # 进入目录 cd minio下载路径:# 下载地址 wget https://dl.min.io/server/minio/release/linux-amd64/minio授权:# 授权 chmod +x minio 2. 自定义配置 自定义账号与登录密码,直接在本…