表达式组装器代码分享

news/2025/3/6 16:42:24/文章来源:https://www.cnblogs.com/throughFog/p/18755799

起因

某些业务,组装的表达式比较长,且、或、括号混杂一块难以立即理解:

public async Task<List<PatientMedicineScheduleInfo>> GetPatientMedicineSchedule(string inpatientID, string hospitalID, DateTime startDate, TimeSpan startTime, DateTime endDate, TimeSpan endTime)
{return await _medicalDbContext.PatientMedicineScheduleInfos.Where(m =>((m.ScheduleDate == startDate.Date && m.ScheduleTime >= startTime) || m.ScheduleDate > startDate.Date)&& (m.ScheduleDate < endDate.Date || (m.ScheduleDate == endDate.Date && m.ScheduleTime <= endTime))&& m.InpatientID == inpatientID && m.HospitalID == hospitalID&& m.DeleteFlag != "*").OrderBy(m => m.ScheduleDate).ThenBy(m => m.ScheduleTime).ThenBy(m => m.GroupID).ThenBy(m => m.HISOrderSort).ToListAsync();
}

我们可以使用表达式树,来对表达式进行分割,提高可读性。通过封装表达式树,实现了表达式组装器ExpBuilder,先看使用:

使用方法

And、Or函数都是静态函数,可以直接通过ExpBuilder.xx来调用。

public async Task<List<PatientMedicineScheduleInfo>> GetPatientMedicineSchedule(string inpatientID, string hospitalID, DateTime startDate, TimeSpan startTime, DateTime endDate, TimeSpan endTime)
{// 将表达式拆分为四个孙表达式Expression<Func<PatientMedicineScheduleInfo, bool>> onDateAfterTime = (PatientMedicineScheduleInfo m) => m.ScheduleDate == startDate.Date && m.ScheduleTime >= startTime;Expression<Func<PatientMedicineScheduleInfo, bool>> afterDate = (PatientMedicineScheduleInfo m) => m.ScheduleDate > startDate.Date;Expression<Func<PatientMedicineScheduleInfo, bool>> onDateBeforeTime = (PatientMedicineScheduleInfo m) => m.ScheduleDate == endDate.Date && m.ScheduleTime <= endTime;Expression<Func<PatientMedicineScheduleInfo, bool>> beforeDate = (PatientMedicineScheduleInfo m) => m.ScheduleDate < endDate.Date;// 使用or函数,组装出两个子表达式var onDateAfter = ExpBuilder.Or(onDateAfterTime, afterDate);var onDateBefore = ExpBuilder.Or(onDateBeforeTime, beforeDate);// 最后使用And组装两个子表达式var dateBetween = ExpBuilder.And(onDateAfter, onDateBefore);return await _medicalDbContext.PatientMedicineScheduleInfos// 使用.Where(dateBetween).Where(m => m.InpatientID == inpatientID && m.HospitalID == hospitalID && m.DeleteFlag != "*").OrderBy(m => m.ScheduleDate).ThenBy(m => m.ScheduleTime).ThenBy(m => m.GroupID).ThenBy(m => m.HISOrderSort).ToListAsync();
}

条件拼接

如果表达式本身是有某些条件才拼接的,可以使用IfAndIfOr

// 表达式后跟两个条件表达式,若均为false,则表达式恒返回true
var apInterventionPredicate = ExpBuilder.True<QuarterPlanWorkInfo>()// 条件一:若excludeApInterventionIDs不为空,则根据其排除部分数据.IfAnd(excludeApInterventionIDs.Length > 0, m => !excludeApInterventionIDs.Contains(m.APInterventionID))// 条件二:若includeApInterventionID有值,则根据其筛选对应数据.IfAnd(includeApInterventionID.HasValue, m => m.APInterventionID == includeApInterventionID.Value);

源代码

ExpBuilder.cs
public static class ExpBuilder
{public static Expression<Func<T, bool>> True<T>(){return f => true;}public static Expression<Func<T, bool>> False<T>(){return f => false;}/// <summary>/// 组合表达式/// </summary>/// <typeparam name="T"></typeparam>/// <param name="first">原表达式</param>/// <param name="second">拼接表达式</param>/// <param name="merge">合并方式函数</param>/// <returns></returns>private static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second,Func<Expression, Expression, Expression> merge){// build parameter map (from parameters of second to parameters of first)  var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);// replace parameters in the second lambda expression with parameters from the first  var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);// apply composition of lambda expression bodies to parameters from the first expression   return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);}/// <summary>/// 表达式且/// </summary>/// <typeparam name="T">类型</typeparam>/// <param name="first">原表达式</param>/// <param name="second">拼接表达式</param>/// <returns></returns>public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second){return first.Compose(second, Expression.AndAlso);}/// <summary>/// 条件表达式且/// </summary>/// <typeparam name="T">类型</typeparam>/// <param name="first">原表达式</param>/// <param name="condition">条件</param>/// <param name="second">拼接表达式</param>/// <returns></returns>public static Expression<Func<T, bool>> IfAnd<T>(this Expression<Func<T, bool>> first, bool condition, Expression<Func<T, bool>> second){return condition ? first.Compose(second, Expression.AndAlso) : first;}/// <summary>/// 表达式或/// </summary>/// <typeparam name="T">类型</typeparam>/// <param name="first">原表达式</param>/// <param name="second">拼接表达式</param>/// <returns></returns>public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second){return first.Compose(second, Expression.OrElse);}/// <summary>/// 条件表达式或/// </summary>/// <typeparam name="T">类型</typeparam>/// <param name="first">原表达式</param>/// <param name="condition">条件</param>/// <param name="second">拼接表达式</param>/// <returns></returns>public static Expression<Func<T, bool>> IfOr<T>(this Expression<Func<T, bool>> first, bool condition, Expression<Func<T, bool>> second){return condition ? first.Compose(second, Expression.OrElse) : first;}
}
ParameterRebinder.cs
public class ParameterRebinder : ExpressionVisitor
{private readonly Dictionary<ParameterExpression, ParameterExpression> map;public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map){this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();}internal static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp){return new ParameterRebinder(map).Visit(exp);}protected override Expression VisitParameter(ParameterExpression p){if (map.TryGetValue(p, out ParameterExpression replacement)) p = replacement;return base.VisitParameter(p);}
}

单元测试

public class ExBuilderTest
{/// <summary>/// 且表达式/// </summary>[Fact]public void AndTest(){Expression<Func<int, bool>> exp1 = (_) => true;Expression<Func<int, bool>> exp2 = (_) => false;var combined = exp1.And(exp2); // exp1 && exp2Assert.False(combined.Compile()(0));}/// <summary>/// 或表达式/// </summary>/// <example>/// exp1 || exp2/// </example>[Fact]public void OrTest(){Expression<Func<int, bool>> exp1 = (_) => true;Expression<Func<int, bool>> exp2 = (_) => false;var combined = exp1.Or(exp2); // exp1 || exp2Assert.True(combined.Compile()(0));}/// <summary>/// 条件且表达式/// </summary>/// <example>/// if (condition) exp1 && exp2/// </example>[Fact]public void IfAndTest(){Expression<Func<int, bool>> exp1 = (_) => true;Expression<Func<int, bool>> exp2 = (_) => false;var combined1 = exp1.IfAnd(true, exp2); // true, exp1 && exp2Assert.False(combined1.Compile()(0));var combined2 = exp1.IfAnd(false, exp2); // false, exp1Assert.True(combined2.Compile()(0));}/// <summary>/// 条件或表达式/// </summary>/// <example>/// if (condition) exp1 || exp2/// </example>[Fact]public void IfOrTest(){Expression<Func<int, bool>> exp1 = (_) => false;Expression<Func<int, bool>> exp2 = (_) => true;var combined1 = exp1.IfOr(true, exp2); // true, exp1 || exp2Assert.True(combined1.Compile()(0));var combined2 = exp1.IfOr(false, exp2); // false, exp1Assert.False(combined2.Compile()(0));}/// <summary>/// 且表达式与或表达式组合/// </summary>[Fact]public void AndCombineOr(){Expression<Func<int, bool>> exp1 = (_) => false;Expression<Func<int, bool>> exp2 = (_) => true;Expression<Func<int, bool>> exp3 = (_) => true;var combined1 = exp1.And(exp2).Or(exp3);Assert.True(combined1.Compile()(0)); // (exp1 && exp2) || exp3var combined2 = exp1.And(exp2.Or(exp3));Assert.False(combined2.Compile()(0)); // exp1 && (exp2 || exp3)}
}

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

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

相关文章

Easyexcel(3-文件导出)

EasyExcel 是一个便捷、高效的Excel读写库,尤其在大数据量的文件导出场景中表现出色。其核心优势在于减少内存占用和提升数据处理速度,适合用于Java项目中大批量数据的导出。响应头设置 通过设置文件导出的响应头,可以自定义文件导出的名字信息等 //编码格式为UTF-8 respons…

天嵌通途xczu15eg学习笔记——PL端DDR4的MIG IP核配置及测试过程

首先这是PL端的DDR4芯片,型号为MT40A256M16GE-075E,256Mx16,即512MB,由于不能上传附件,所以PDF还请大家自行上网搜索下载。DDR4参数的获取参考了以下两个CSDN的博客。 https://ztzhang.blog.csdn.net/article/details/142444632 https://blog.csdn.net/apple_53311083/art…

ThingsPanel与ThingsBoard:深度对比与选型剖析

在物联网平台领域,ThingsPanel和ThingsBoard各有千秋,为助大家明晰二者差异、精准选型,以下将从多个核心维度展开深度解读:ThingsPanelThingsBoard一、开源协议与版本模式开源协议: ThingsPanel遵循AGPLv3.0协议。 ThingsBoard采用Apache2.0协议。 版本提供:二者均配备社…

2025 年开源替代方案为何正在取代 OutSystems?技术自由度与成本优势深度解析

2025 年企业为何转向开源?揭秘 OutSystems 四大痛点,推荐 NocoBase、Appsmith、Budibase 等开源方案,帮助你的团队实现技术自由+成本优势双赢。原文链接:https://www.nocobase.com/cn/blog/outsystems-open-source-alternatives OutSystems 的隐藏成本不只是金钱 OutSystem…

rocketmq引入版本不对的问题

原因是,公司电脑可以正常启动,自己的电脑启动报错,报错是缺少了这个类 之后查看版本发现对不上,公司的事4.9.7,自己上面的居然是5.1.4 然后去除版本指定版本,不知道是否有问题,反正启动没问题了,不过部署还是用公司电脑吧,怕出问题<dependency><groupId>o…

navicat如何导出数据?附navicat15安装包

前言 大家好,我是小徐啊。navicat是我们常用的数据库连接工具,其功能十分强大。可以说是我使用过的各个连接工具中,最强大的工具。今天,小徐就来介绍下如何在navicat里面导出数据。文末附navicat15获取方式。 如何导出数据 首先,打开navicat,然后连接我们要导出的数据库。…

城市犯罪大数据时空分析与预测系统

随着城市化进程的加快,城市犯罪问题日益突出,对社会稳定和居民安全构成严重威胁。为了有效预防和打击犯罪,提升城市管理水平,构建城市犯罪大数据时空分析与预测系统显得尤为重要。本文将从系统建设内容的角度,探讨如何利用大数据和遥感技术,为城市安全保驾护航。一、系统…

华为云昇腾专区重磅上线!带你入门昇腾AI技术与DeepSeek实践

华为云昇腾专区重磅上线!专区围绕DeepSeek对话、互动式课程体系、业界主流大模型、免费实践环境四大核心能力,打造“体验—学习—应用”闭环,助力开发者实现AI转型“零门槛”跃迁。摘要:专区围绕DeepSeek对话、互动式课程体系、业界主流大模型、免费实践环境四大核心能力,…

基因测序芯片(Flowcell)加工工艺 -原子能和替代能源委员会生物梅里埃公司

一、公司简介: 原子能和替代能源委员会生物梅里埃公司 二、主流产品三、专利检索 ★专利检索1: ★专利: US8647465B2-Method of bonding microstructured substrates-2003 ★测序芯片(Flowcell)类型: 纳米孔FC ★加工工艺 1、一种含有微结构(5-50um)的基板的胶水粘接方…

React18 04 JSX底层渲染机制--创建虚拟DOM与真实DOM

关于JSX底层处理机制 1)首先,编写的JSX语法,编译为虚拟DOM对象(virtualDOM) 虚拟DOM对象:框架内部构建的一套对象体系(对象的相关成员都是React内部规定的),基于这些属性描述出所构建视图中的DOM节点的相关特征。 a. 基于babel-preset-react-app,把JSX编译为React.cr…

欢迎加入ARM64技术手册知识库

本文来自博客园,作者:dolinux,未经同意,禁止转载