前言

日常开发时,使用Linq和EF经常会在存在多条件查询,或者说动态条件查询时,便存在合并表达式树的情况。基于这种情况结合一些资料,写了个扩展类,代码如下:

代码实现

    /// <summary>
/// Linq表达式扩展方法
/// </summary>
public static class PredicateExtensions
{
/// <summary>
/// 以And合并单个表达式
/// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
/// </summary>
public static Expression<Func<T, bool>> MergeAnd<T>(this Expression<Func<T, bool>> leftExpress, Expression<Func<T, bool>> rightExpress)
{
//声明传递参数(也就是表达式树里面的参数别名s)
ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
//统一管理参数,保证参数一致,否则会报错
var visitor = new PredicateExpressionVisitor(parameter);
//表达式树内容
Expression left = visitor.Visit(leftExpress.Body);
Expression right = visitor.Visit(rightExpress.Body);
//合并表达式
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left, right), parameter);
} /// <summary>
/// 以And合并多个表达式
/// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
/// </summary>
public static Expression<Func<T, bool>> MergeAnd<T>(this Expression<Func<T, bool>> express, params Expression<Func<T, bool>>[] arrayExpress)
{
if (!arrayExpress?.Any() ?? true) return express;
//声明传递参数(也就是表达式树里面的参数别名s)
ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
//统一管理参数,保证参数一致,否则会报错
var visitor = new PredicateExpressionVisitor(parameter);
Expression<Func<T, bool>> result = null;
//合并表达式
foreach (var curExpression in arrayExpress)
{
//表达式树内容
Expression left = visitor.Visit(result.Body);
Expression right = visitor.Visit(curExpression.Body);
result = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left, right), parameter);
}
return result;
} /// <summary>
/// 以Or合并表达式
/// 此处采用OrElse实现“最短路径”,避免掉额外且不需要的比较运算式
/// </summary>
public static Expression<Func<T, bool>> MergeOr<T>(this Expression<Func<T, bool>> leftExpress, Expression<Func<T, bool>> rightExpress)
{
//声明传递参数(也就是表达式树里面的参数别名s)
ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
//统一管理参数,保证参数一致,否则会报错
var visitor = new PredicateExpressionVisitor(parameter);
//表达式树内容
Expression left = visitor.Visit(leftExpress.Body);
Expression right = visitor.Visit(rightExpress.Body);
//合并表达式
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(left, right), parameter);
} /// <summary>
/// 以Or合并多个表达式
/// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
/// </summary>
public static Expression<Func<T, bool>> MergeOr<T>(this Expression<Func<T, bool>> express, params Expression<Func<T, bool>>[] arrayExpress)
{
if (!arrayExpress?.Any() ?? true) return express;
//声明传递参数(也就是表达式树里面的参数别名s)
ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
//统一管理参数,保证参数一致,否则会报错
var visitor = new PredicateExpressionVisitor(parameter);
Expression<Func<T, bool>> result = null;
//合并表达式
foreach (var curExpression in arrayExpress)
{
//表达式树内容
Expression left = visitor.Visit(result.Body);
Expression right = visitor.Visit(curExpression.Body);
result = Expression.Lambda<Func<T, bool>>(Expression.OrElse(left, right), parameter);
}
return result;
}
} public class PredicateExpressionVisitor : ExpressionVisitor
{
public ParameterExpression _parameter { get; set; } public PredicateExpressionVisitor(ParameterExpression parameter)
{
_parameter = parameter;
}
protected override Expression VisitParameter(ParameterExpression p)
{
return _parameter;
} public override Expression Visit(Expression expression)
{
//Visit会根据VisitParameter()方法返回的Expression进行相关变量替换
return base.Visit(expression);
}
}

使用例子

    class Program
{ static void Main(string[] args)
{
var models = new List<JsonData>() { new JsonData() { Id = "001", Name = "One" }, new JsonData() { Id = "002", Name = "Tow" } }; Console.WriteLine($"未处理集合:{string.Join(',', models.Select(o => o.Id))}");
//表达式1
Expression<Func<JsonData, bool>> expression1 = t => t.Id == "001";
//表达式2
Expression<Func<JsonData, bool>> expression2 = t => t.Name == "Tow";
//合并成 t => t.Id=="001" && t.Name=="One"
Expression<Func<JsonData, bool>> allEexpression = expression1.MergeAnd(expression2);
Console.WriteLine(allEexpression.Body.ToString());
Console.WriteLine($"已处理集合(And):{string.Join(',', models.Where(expression1.MergeAnd(expression2).Compile()).Select(o => o.Id))}");
//合并成 t => t.Id=="001" || t.Name=="One"
allEexpression = expression1.MergeOr(expression2);
Console.WriteLine(allEexpression.Body.ToString());
Console.WriteLine($"已处理集合(Or):{string.Join(',', models.Where(allEexpression.Compile()).Select(o => o.Id))}"); Console.ReadKey();
}
} public class JsonData
{
public string Id { get; set; }
public string Name { get; set; }
}

结果

相关资料

MSDN Expression类
MSDN ExpressionVisitor类

C# Linq、Lambda表达式树动态构建、合并条件扩展方法的更多相关文章

  1. 通过LINQ表达式树动态构建查询条件

    第一种方法: public static class PredicateExtensions { public static Expression<Func<T, bool>> ...

  2. LINQ to SQL 运行时动态构建查询条件

    在进行数据查询时,经常碰到需要动态构建查询条件.使用LINQ实现这个需求可能会比以前拼接SQL语句更麻烦一些.本文介绍了3种运行时动态构建查询条件的方法.本文中的例子最终实现的都是同一个功能,从Nor ...

  3. C# Lambda表达式详解,及Lambda表达式树的创建

    最近由于项目需要,刚刚学完了Action委托和Func<T>委托,发现学完了委托就必须学习lambda表达式,委托和Lambda表达式联合起来,才能充分的体现委托的便利.才能使代码更加简介 ...

  4. 将简单的lambda表达式树转为对应的sqlwhere条件

    1.Lambda的介绍 园中已经有很多关于lambda的介绍了.简单来讲就是vs编译器给我带来的语法糖,本质来讲还是匿名函数.在开发中,lambda给我们带来了很多的简便.关于lambda的演变过程可 ...

  5. EntityFramework动态多条件查询与Lambda表达式树

              在常规的信息系统中, 我们有需要动态多条件查询的情况, 例如UI上有多个选择项可供用户选择多条件查询数据. 那么在.net平台Entity Framework下, 我们用Lambd ...

  6. 表达式树动态拼接lambda

    动态拼接lambda表达式树   前言 最近在优化同事写的代码(我们的框架用的是dapperLambda),其中有一个这样很普通的场景——界面上提供了一些查询条件框供用户来进行过滤数据.由于dappe ...

  7. 动态拼接lambda表达式树

    前言 最近在优化同事写的代码(我们的框架用的是dapperLambda),其中有一个这样很普通的场景——界面上提供了一些查询条件框供用户来进行过滤数据.由于dapperLambda按条件查询时是传入表 ...

  8. Lambda表达式树构建(上)

    概述 Lambda是C#常用的语句,采用委托等方式,来封装真实的代码块.Lambda其实就是语法糖,是一个匿名函数,是一种高效的类似于函数式编程的表达式,Lambda简化了开发中需要编写的代码量.它可 ...

  9. 追根溯源之Linq与表达式树

    一.什么是表达式树?   首先来看下官方定义(以下摘录自巨硬官方文档)   表达式树表示树状数据结构中的代码,其中每个节点都是表达式,例如,方法调用或诸如的二进制操作x < y.   您可以编译 ...

  10. C#学习笔记(九):LINQ和表达式树

    LINQ LINQ:语言集成查询(Language Integrated Query)是一组用于c#和Visual Basic语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同 ...

随机推荐

  1. openGauss事务机制中MVCC技术的实现分析

    openGauss 事务机制中 MVCC 技术的实现分析 概述 事务 事务是为用户提供的最核心.最具吸引力的数据库功能之一.简单地说,事务是用户定义的一系列数据库操作(如查询.插入.修改或删除等)的集 ...

  2. 建议收藏!Harmony应用配置文件概述(Stage模型)

    一. 应用配置文件 每个应用项目必须在项目的代码目录下加入配置文件,这些配置文件会向编译工具.操作系统和应用市场提供应用的基本信息. 在基于Stage模型开发的应用项目代码下,都存在一个app.jso ...

  3. 面试连环炮系列(二十️四):为什么选择RocketMQ

    为什么选择RocketMQ,而不是其他MQ 性能:阿里支撑,经受住淘宝,天猫双11重重考验:性能高:可靠性好:可用性高:易扩展. 功能:功能完善,我们需要的功能基本都够满足,如:事务消息,消息重试,死 ...

  4. linux 忘记密码怎么破?

    前言 适合硬件在自己旁边的,不适合云服务器,云服务器很方便的,可以直接重置密码,因为云服务器都是虚拟机. 正文 1.步骤 进入到可以看到节目的视图,按下esc键进入下面的视图! 2.接着进入下面的界面 ...

  5. mm系列权重文件瘦身

    瘦身脚本: (会在resnet50.pth文件的同级目录下生成一个resnet50_thin.pth) import os import torch root_dir = os.getcwd() de ...

  6. Serverless在游戏运营行业进行数据采集分析的最佳实践

    简介: 这个架构不光适用于游戏运营行业,其实任何大数据采集传输的场景都是适用的,目前也已经有很多客户正在基于Serverless的架构跑在生产环境,或者正走在改造Serverless 架构的路上. 众 ...

  7. 唯品会:在 Flink 容器化与平台化上的建设实践

    简介: 唯品会 Flink 的容器化实践应用,Flink SQL 平台化建设,以及在实时数仓和实验平台上的应用案例. 转自dbaplus社群公众号作者:王康,唯品会数据平台高级开发工程师 自 2017 ...

  8. [FE] uni-app 导航栏开发指南

    一种是 原生导航栏添加自定义按钮.简单明了. pages.json 配置 { "path": "pages/log/log", "style" ...

  9. [FAQ] Vue iframe 的 src 是链接地址却加载了相对路径 ?

    iframe 的 src 是链接, 但是加载的实际链接是相对路径,只有一种可能:链接地址不正确. 检查链接有没有少符号,常见错误:http//,http:/ Refer:Vue的iframe错误 Li ...

  10. [PHP] 浅谈 Laravel 三大验证方式的区别, auth:api, passport, auth:airlock

    auth:api 最先出来,提供了最简单和最实用的方式进行 api 身份校验. 关于它的含义和用法你可以参考以下两篇: 浅谈 Laravel Authentication 的 auth:api 浅谈 ...